Auswertung Experiment

Database tüddelei
DBHanlder läuft gut für DIESE Datanbank struktur...
App begonnen aber weit entfernt von gutem Stand
This commit is contained in:
sioe
2024-11-15 16:51:37 +01:00
parent 553ed19b9f
commit 397cfa61dd
219 changed files with 584 additions and 854 deletions

View File

@@ -0,0 +1,101 @@
This test module is part of matlab2tikz.
Its use is mainly of interest to the matlab2tikz developers to assert that the produced output is good.
Ideally, the tests should be run on every supported environment, i.e.:
* MATLAB R2014a/8.3 (or an older version)
* MATLAB R2014b/8.4 (or a newer version)
* Octave 3.8
Preparing your environment
==========================
Before you can run the tests, you need to make sure that you have all relevant
functions available on your path. From within the `/test` directory run the
following code in your MATLAB/Octave console:
```matlab
addpath(pwd); % for the test harness
addpath(fullfile(pwd,'..','src')); % for matlab2tikz
addpath(fullfile(pwd,'suites')); % for the test suites
```
Running the tests
=================
We have two kinds of tests runners available that each serve a slightly different
purpose.
* "Graphical" tests produce an output report that graphically shows test
figures as generated by MATLAB/Octave and our TikZ output.
* "Headless" tests do not produce graphical output, but instead check the MD5
hash of the generated TikZ files to make sure that the same output
as before is generated.
It is recommended to run the headless tests first and check the problems in
the graphical tests afterwards.
Headless tests
--------------
These tests check that the TikZ output file produced by `matlab2tikz` matches
a reference output. The actual checking is done by hashing the file and the
corresponding hashes are stored in `.md5` files next to the test suites.
For each environment, different reference hashes can be stored.
The headless tests can be invoked using
```matlab
testHeadless;
```
, or, equivalently,
```matlab
makeTravisReport(testHeadless)
```
There are some caveats for this method of testing:
* The MD5 hash is extremely brittle to small details in the output: e.g.
extra whitespace or some other characters will change the hash.
* This automated test does NOT test whether the output is desirable or not.
It only checks whether the previous output is not altered!
* Hence, when structural changes are made, the reference hash should be changed.
This SHOULD be motivated in the pull request (e.g. with a picture)!
Graphical tests
---------------
These tests allow easy comparison of a native PDF `print` output and the
output produced by `matlab2tikz`. For the large amount of cases, however,
this comparison has become somewhat unwieldy.
You can execute the tests using
```matlab
testGraphical;
```
or, equivalently,
```matlab
makeLatexReport(testGraphical)
```
This generates a LaTeX report in `test/output/current/acid.tex` which can then be compiled.
Compilation of this file can be done using the Makefile `test/output/current/Makefile` if you are on a Unix-like system (OS X, Linux) or have [Cygwin](https://www.cygwin.com) installed on Windows.
If all goes well, the result will be the file `test/output/current/acid.pdf` that contains
a list of the test figures, exported as PDF and right next to it the matlab2tikz generated plot.
Advanced Use
------------
Both `testHeadless` and `testGraphical` can take multiple arguments, those are documented in the raw test runner `testMatlab2tikz` that is used behind the scenes. Note that this file sits in a private directory, so `help testMatlab2tikz` will not work!
Also, both can be called with a single output argument, for programmatical
access to the test results as
```matlab
status = testHeadless()
```
These test results in `status` can be passed to `saveHashTable` for updating the hash tables.
Obviously, this should be done with the due diligence!
Automated Tests
===============
The automated tests run on [Travis-CI](https://travis-ci.org) for Octave and on a [personal Jenkins server](https://github.com/matlab2tikz/matlab2tikz/wiki/Jenkins) for MATLAB.
These are effectively the "headless" tests that get called by the `runMatlab2TikzTests` function.
Without verification of those automated tests, a pull request is unlikely to get merged.

View File

@@ -0,0 +1,280 @@
function [ report ] = codeReport( varargin )
%CODEREPORT Builds a report of the code health
%
% This function generates a Markdown report on the code health. At the moment
% this is limited to the McCabe (cyclomatic) complexity of a function and its
% subfunctions.
%
% This makes use of |checkcode| in MATLAB.
%
% Usage:
%
% CODEREPORT('function', functionName) to determine which function is
% analyzed. (default: matlab2tikz)
%
% CODEREPORT('complexityThreshold', integer ) to set above which complexity, a
% function is added to the report (default: 10)
%
% CODEREPORT('stream', stream) to set to which stream/file to output the report
% (default: 1, i.e. stdout). The stream is used only when no output argument
% for `codeReport` is specified!.
%
% See also: checkcode, mlint
SM = StreamMaker();
%% input options
ipp = m2tInputParser();
ipp = ipp.addParamValue(ipp, 'function', 'matlab2tikz', @ischar);
ipp = ipp.addParamValue(ipp, 'complexityThreshold', 10, @isnumeric);
ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
ipp = ipp.parse(ipp, varargin{:});
stream = SM.make(ipp.Results.stream, 'w');
%% generate report data
data = checkcode(ipp.Results.function,'-cyc','-struct');
[complexityAll, mlintMessages] = splitCycloComplexity(data);
%% analyze cyclomatic complexity
categorizeComplexity = @(x) categoryOfComplexity(x, ...
ipp.Results.complexityThreshold, ...
ipp.Results.function);
complexityAll = arrayfun(@parseCycloComplexity, complexityAll);
complexityAll = arrayfun(categorizeComplexity, complexityAll);
complexity = filter(complexityAll, @(x) strcmpi(x.category, 'Bad'));
complexity = sortBy(complexity, 'line', 'ascend');
complexity = sortBy(complexity, 'complexity', 'descend');
[complexityStats] = complexityStatistics(complexityAll);
%% analyze other messages
%TODO: handle all mlint messages and/or other metrics of the code
%% format report
dataStr = complexity;
dataStr = arrayfun(@(d) mapField(d, 'function', @markdownInlineCode), dataStr);
if ~isempty(dataStr)
dataStr = addFooterRow(dataStr, 'complexity', @sum, {'line',0, 'function',bold('Total')});
end
dataStr = arrayfun(@(d) mapField(d, 'line', @integerToString), dataStr);
dataStr = arrayfun(@(d) mapField(d, 'complexity', @integerToString), dataStr);
report = makeTable(dataStr, {'function', 'complexity'}, ...
{'Function', 'Complexity'});
%% command line usage
if nargout == 0
if ismember(stream.name, {'stdout','stderr'})
stream.print('%s\n', codelinks(report, ipp.Results.function));
else
stream.print('%s\n', report);
end
figure('name',sprintf('Complexity statistics of %s', ipp.Results.function));
h = statisticsPlot(complexityStats, 'Complexity', 'Number of functions');
for hh = h
plot(hh, [1 1]*ipp.Results.complexityThreshold, ylim(hh), ...
'k--','DisplayName','Threshold');
end
legend(h(1),'show','Location','NorthEast');
clear report
end
end
%% CATEGORIZATION ==============================================================
function [complexity, others] = splitCycloComplexity(list)
% splits codereport into McCabe complexity and others
filter = @(l) ~isempty(strfind(l.message, 'McCabe complexity'));
idxComplexity = arrayfun(filter, list);
complexity = list( idxComplexity);
others = list(~idxComplexity);
end
function [data] = categoryOfComplexity(data, threshold, mainFunc)
% categorizes the complexity as "Good", "Bad" or "Accepted"
TOKEN = '#COMPLEX'; % token to signal allowed complexity
try %#ok
helpStr = help(sprintf('%s>%s', mainFunc, data.function));
if ~isempty(strfind(helpStr, TOKEN))
data.category = 'Accepted';
return;
end
end
if data.complexity > threshold
data.category = 'Bad';
else
data.category = 'Good';
end
end
%% PARSING =====================================================================
function [out] = parseCycloComplexity(in)
% converts McCabe complexity report strings into a better format
out = regexp(in.message, ...
'The McCabe complexity of ''(?<function>[A-Za-z0-9_]+)'' is (?<complexity>[0-9]+).', ...
'names');
out.complexity = str2double(out.complexity);
out.line = in.line;
end
%% DATA PROCESSING =============================================================
function selected = filter(list, filterFunc)
% filters an array according to a binary function
idx = logical(arrayfun(filterFunc, list));
selected = list(idx);
end
function [data] = mapField(data, field, mapping)
data.(field) = mapping(data.(field));
end
function sorted = sortBy(list, fieldName, mode)
% sorts a struct array by a single field
% extra arguments are as for |sort|
values = arrayfun(@(m)m.(fieldName), list);
[dummy, idxSorted] = sort(values(:), 1, mode); %#ok
sorted = list(idxSorted);
end
function [stat] = complexityStatistics(list)
% calculate some basic statistics of the complexities
stat.values = arrayfun(@(c)(c.complexity), list);
stat.binCenter = sort(unique(stat.values));
categoryPerElem = {list.category};
stat.categories = unique(categoryPerElem);
nCategories = numel(stat.categories);
groupedHist = zeros(numel(stat.binCenter), nCategories);
for iCat = 1:nCategories
category = stat.categories{iCat};
idxCat = ismember(categoryPerElem, category);
groupedHist(:,iCat) = hist(stat.values(idxCat), stat.binCenter);
end
stat.histogram = groupedHist;
stat.median = median(stat.values);
end
function [data] = addFooterRow(data, column, func, otherFields)
% adds a footer row to data table based on calculations of a single column
footer = data(end);
for iField = 1:2:numel(otherFields)
field = otherFields{iField};
value = otherFields{iField+1};
footer.(field) = value;
end
footer.(column) = func([data(:).(column)]);
data(end+1) = footer;
end
%% FORMATTING ==================================================================
function str = integerToString(value)
% convert integer to string
str = sprintf('%d',value);
end
function str = markdownInlineCode(str)
% format as inline code for markdown
str = sprintf('`%s`', str);
end
function str = makeTable(data, fields, header)
% make a markdown table from struct array
nData = numel(data);
str = '';
if nData == 0
return; % empty input
end
%TODO: use gfmTable from makeTravisReport instead to do the formatting
% determine column sizes
nFields = numel(fields);
table = cell(nFields, nData);
columnWidth = zeros(1,nFields);
for iField = 1:nFields
field = fields{iField};
table(iField, :) = {data(:).(field)};
columnWidth(iField) = max(cellfun(@numel, table(iField, :)));
end
columnWidth = max(columnWidth, cellfun(@numel, header));
columnWidth = columnWidth + 2; % empty space left and right
columnWidth([1,end]) = columnWidth([1,end]) - 1; % except at the edges
% format table inside cell array
table = [header; table'];
for iField = 1:nFields
FORMAT = ['%' int2str(columnWidth(iField)) 's'];
for jData = 1:size(table, 1)
table{jData, iField} = strjust(sprintf(FORMAT, ...
table{jData, iField}), 'center');
end
end
% insert separator
table = [table(1,:)
arrayfun(@(n) repmat('-',1,n), columnWidth, 'UniformOutput',false)
table(2:end,:)]';
% convert cell array to string
FORMAT = ['%s' repmat('|%s', 1,nFields-1) '\n'];
str = sprintf(FORMAT, table{:});
end
function str = codelinks(str, functionName)
% replaces inline functions with clickable links in MATLAB
str = regexprep(str, '`([A-Za-z0-9_]+)`', ...
['`<a href="matlab:edit ' functionName '>$1">$1</a>`']);
%NOTE: editing function>subfunction will focus on that particular subfunction
% in the editor (this also works for the main function)
end
function str = bold(str)
str = ['**' str '**'];
end
%% PLOTTING ====================================================================
function h = statisticsPlot(stat, xLabel, yLabel)
% plot a histogram and box plot
nCategories = numel(stat.categories);
colors = colorscheme;
h(1) = subplot(5,1,1:4);
hold all;
hb = bar(stat.binCenter, stat.histogram, 'stacked');
for iCat = 1:nCategories
category = stat.categories{iCat};
set(hb(iCat), 'DisplayName', category, 'FaceColor', colors.(category), ...
'LineStyle','none');
end
%xlabel(xLabel);
ylabel(yLabel);
h(2) = subplot(5,1,5);
hold all;
boxplot(stat.values,'orientation','horizontal',...
'boxstyle', 'outline', ...
'symbol', 'o', ...
'colors', colors.All);
xlabel(xLabel);
xlims = [min(stat.binCenter)-1 max(stat.binCenter)+1];
c = 1;
ylims = (ylim(h(2)) - c)/3 + c;
set(h,'XTickMode','manual','XTick',stat.binCenter,'XLim',xlims);
set(h(1),'XTickLabel','');
set(h(2),'YTickLabel','','YLim',ylims);
linkaxes(h, 'x');
end
function colors = colorscheme()
% recognizable color scheme for the categories
colors.All = [ 0 113 188]/255;
colors.Good = [118 171 47]/255;
colors.Bad = [161 19 46]/255;
colors.Accepted = [236 176 31]/255;
end

View File

@@ -0,0 +1,256 @@
function compareTimings(statusBefore, statusAfter)
% COMPARETIMINGS compare timing of matlab2tikz test suite runs
%
% This function plots some analysis plots of the timings of different test
% cases. When the test suite is run repeatedly, the median statistics are
% reported as well as the individual runs.
%
% Usage:
% COMPARETIMINGS(statusBefore, statusAfter)
%
% Parameters:
% - statusBefore and statusAfter are expected to be
% N x R cell arrays, each cell contains a status of a test case
% where there are N test cases, repeated R times each.
%
% You can build such cells, e.g. with the following snippet.
%
% suite = @ACID
% N = numel(suite(0)); % number of test cases
% R = 10; % number of repetitions of each test case
%
% statusBefore = cell(N, R);
% for r = 1:R
% statusBefore(:, r) = testHeadless;
% end
%
% % now check out the after commit
%
% statusAfter = cell(N, R);
% for r = 1:R
% statusAfter(:, r) = testHeadless;
% end
%
% compareTimings(statusBefore, statusAfter)
%
% See also: testHeadless
%% Extract timing information
time_cf = extract(statusBefore, statusAfter, @(s) s.tikzStage.cleanfigure_time);
time_m2t = extract(statusBefore, statusAfter, @(s) s.tikzStage.m2t_time);
%% Construct plots
hax(1) = subplot(3,2,1);
histograms(time_cf, 'cleanfigure');
legend('show')
hax(2) = subplot(3,2,3);
histograms(time_m2t, 'matlab2tikz');
legend('show')
linkaxes(hax([1 2]),'x');
hax(3) = subplot(3,2,5);
histogramSpeedup('cleanfigure', time_cf, 'matlab2tikz', time_m2t);
legend('show');
hax(4) = subplot(3,2,2);
plotByTestCase(time_cf, 'cleanfigure');
legend('show')
hax(5) = subplot(3,2,4);
plotByTestCase(time_m2t, 'matlab2tikz');
legend('show')
hax(6) = subplot(3,2,6);
plotSpeedup('cleanfigure', time_cf, 'matlab2tikz', time_m2t);
legend('show');
linkaxes(hax([4 5 6]), 'x');
% ------------------------------------------------------------------------------
end
%% Data processing
function timing = extract(statusBefore, statusAfter, func)
otherwiseNaN = {'ErrorHandler', @(varargin) NaN};
timing.before = cellfun(func, statusBefore, otherwiseNaN{:});
timing.after = cellfun(func, statusAfter, otherwiseNaN{:});
end
function [names,timings] = splitNameTiming(vararginAsCell)
names = vararginAsCell(1:2:end-1);
timings = vararginAsCell(2:2:end);
end
%% Plot subfunctions
function [h] = histograms(timing, name)
% plot histogram of time measurements
colors = colorscheme;
histostyle = {'DisplayStyle', 'bar',...
'Normalization','pdf',...
'EdgeColor','none',...
'BinWidth',0.025};
hold on;
h{1} = myHistogram(timing.before, histostyle{:}, ...
'FaceColor', colors.before, ...
'DisplayName', 'Before');
h{2} = myHistogram(timing.after , histostyle{:}, ...
'FaceColor', colors.after,...
'DisplayName', 'After');
xlabel(sprintf('%s runtime [s]',name))
ylabel('Empirical PDF');
end
function [h] = histogramSpeedup(varargin)
% plot histogram of observed speedup
histostyle = {'DisplayStyle', 'bar',...
'Normalization','pdf',...
'EdgeColor','none'};
[names,timings] = splitNameTiming(varargin);
nData = numel(timings);
h = cell(nData, 1);
minTime = NaN; maxTime = NaN;
for iData = 1:nData
name = names{iData};
timing = timings{iData};
hold on;
speedup = computeSpeedup(timing);
color = colorOptionsOfName(name, 'FaceColor');
h{iData} = myHistogram(speedup, histostyle{:}, color{:},...
'DisplayName', name);
[minTime, maxTime] = minAndMax(speedup, minTime, maxTime);
end
xlabel('Speedup')
ylabel('Empirical PDF');
set(gca,'XScale','log', 'XLim', [minTime, maxTime].*[0.9 1.1]);
end
function [h] = plotByTestCase(timing, name)
% plot all time measurements per test case
colors = colorscheme;
hold on;
if size(timing.before, 2) > 1
h{3} = plot(timing.before, '.',...
'Color', colors.before, 'HandleVisibility', 'off');
h{4} = plot(timing.after, '.',...
'Color', colors.after, 'HandleVisibility', 'off');
end
h{1} = plot(median(timing.before, 2), '-',...
'LineWidth', 2, ...
'Color', colors.before, ...
'DisplayName', 'Before');
h{2} = plot(median(timing.after, 2), '-',...
'LineWidth', 2, ...
'Color', colors.after,...
'DisplayName', 'After');
ylabel(sprintf('%s runtime [s]', name));
set(gca,'YScale','log')
end
function [h] = plotSpeedup(varargin)
% plot speed up per test case
[names, timings] = splitNameTiming(varargin);
nDatasets = numel(names);
minTime = NaN;
maxTime = NaN;
h = cell(nDatasets, 1);
for iData = 1:nDatasets
name = names{iData};
timing = timings{iData};
color = colorOptionsOfName(name);
hold on
[speedup, medSpeedup] = computeSpeedup(timing);
if size(speedup, 2) > 1
plot(speedup, '.', color{:}, 'HandleVisibility','off');
end
h{iData} = plot(medSpeedup, color{:}, 'DisplayName', name, ...
'LineWidth', 2);
[minTime, maxTime] = minAndMax(speedup, minTime, maxTime);
end
nTests = size(speedup, 1);
plot([-nTests nTests*2], ones(2,1), 'k','HandleVisibility','off');
legend('show', 'Location','NorthWest')
set(gca,'YScale','log','YLim', [minTime, maxTime].*[0.9 1.1], ...
'XLim', [0 nTests+1])
xlabel('Test case');
ylabel('Speed-up (t_{before}/t_{after})');
end
%% Histogram wrapper
function [h] = myHistogram(data, varargin)
% this is a very crude wrapper that mimics Histogram in R2014a and older
if ~isempty(which('histogram'))
h = histogram(data, varargin{:});
else % no "histogram" available
options = struct(varargin{:});
minData = min(data(:));
maxData = max(data(:));
if isfield(options, 'BinWidth')
numBins = ceil((maxData-minData)/options.BinWidth);
elseif isfield(options, 'NumBins')
numBins = options.NumBins;
else
numBins = 10;
end
[counts, bins] = hist(data(:), numBins);
if isfield(options,'Normalization') && strcmp(options.Normalization,'pdf')
binWidth = mean(diff(bins));
counts = counts./sum(counts)/binWidth;
end
h = bar(bins, counts, 1);
% transfer properties as well
names = fieldnames(options);
for iName = 1:numel(names)
option = names{iName};
if isprop(h, option)
set(h, option, options.(option));
end
end
set(allchild(h),'FaceAlpha', 0.75); % only supported with OpenGL renderer
% but this should look a bit similar with matlab2tikz then...
end
end
%% Calculations
function [speedup, medSpeedup] = computeSpeedup(timing)
% computes the timing speedup (and median speedup)
dRep = 2; % dimension containing the repeated tests
speedup = timing.before ./ timing.after;
medSpeedup = median(timing.before, dRep) ./ median(timing.after, dRep);
end
function [minTime, maxTime] = minAndMax(speedup, minTime, maxTime)
% calculates the minimum/maximum time in an array and peviously
% computed min/max times
minTime = min([minTime; speedup(:)]);
maxTime = min([maxTime; speedup(:)]);
end
%% Color scheme
function colors = colorscheme()
% defines the color scheme
colors.matlab2tikz = [161 19 46]/255;
colors.cleanfigure = [ 0 113 188]/255;
colors.before = [236 176 31]/255;
colors.after = [118 171 47]/255;
end
function color = colorOptionsOfName(name, keyword)
% returns a cell array with a keyword (default: 'Color') and a named color
% if it exists in the colorscheme
if ~exist('keyword','var') || isempty(keyword)
keyword = 'Color';
end
colors = colorscheme;
if isfield(colors,name)
color = {keyword, colors.(name)};
else
color = {};
end
end

View File

@@ -0,0 +1,53 @@
function example_bar_plot()
test_data =[18 0; 20 0; 21 2; 30 14; 35 34; 40 57; 45 65; 50 46; 55 9; 60 2; 65 1; 70 0];
% Create figure
figure1 = figure('Color',[1 1 1]);
subplot(1,2,1)
hb=barh(test_data(:,1),test_data(:,2),'DisplayName','Test Data');
ylabel('parameter [units]');
xlabel('#');
legend('show','Location','northwest');
subplot(1,2,2)
hb=bar(test_data(:,1),test_data(:,2),'DisplayName','Test Data');
xlabel('parameter [units]');
ylabel('#');
legend('show','Location','northwest');
xdata=test_data(:,1);
barWidth=test_getBarWidthInAbsolutUnits(hb);
x_l=xdata-barWidth/2;
x_u=xdata+barWidth/2;
max_y=max(test_data(:,2))*1.2;
x=[];
y=[];
for i=1:length(x_l)
x = [x , x_l(i),x_l(i),nan,x_u(i),x_u(i),nan];
y = [y, 0,max_y ,nan,0 ,max_y ,nan];
end
hold on
plot(x,y,'r');
matlab2tikz('figurehandle',figure1,'filename','example_v_bar_plot.tex' ,'standalone', true);
function BarWidth=test_getBarWidthInAbsolutUnits(h)
% astimates the width of a bar plot
XData_bar=get(h,'XData');
length_bar = length(XData_bar);
BarWidth= get(h, 'BarWidth');
if length_bar > 1
BarWidth = min(diff(XData_bar))*BarWidth;
end

View File

@@ -0,0 +1,80 @@
%% Quiver calculations
% These are calculations for the quiver dimensions as implemented in MATLAB
% (HG1) as in the |quiver.m| function.
%
% For HG2 and Octave, the situation might be different.
%
% A single quiver is defined as:
%
% C
% \
% \
% A ----------------- B
% /
% /
% D
%
% To know the dimensions of the arrow head, MATLAB defines the quantities
% alpha = beta = 0.33 that determine the coordinates of C and D as given below.
clc;
clear variables;
close all;
%% Parameters
try
syms x y z u v w alpha beta epsilon real
catch
warning('Symbolic toolbox not found. Interpret the values with care!');
x = randn(); y = randn(); z = randn();
u = randn(); v = randn(); w = randn();
end
alpha = 0.33;
beta = alpha;
epsilon = 0;
is2D = true;
%% Coordinates as defined in MATLAB
% Note that in 3D, the arrow head is oriented in a weird way. Let' just ignore
% that and only focus on 2D and use the same in 3D. Due to the lack
% of [u,v,w]-symmetry in those equations, the angle is bound to depend on the
% length of |delta|, i.e. something we don't know beforehand.
A = [x y z].';
delta = [u v w].';
B = A + delta;
C = B - alpha*[u+beta*(v+epsilon);
v-beta*(u+epsilon)
w];
D = B - alpha*[u-beta*(v+epsilon);
v+beta*(u+epsilon)
w];
if is2D
A = A(1:2);
B = B(1:2);
C = C(1:2);
D = D(1:2);
delta = delta(1:2);
end
%% Calculating the angle of the arrowhead
% Calculate the cos(angle) using the inner product
unitVector = @(v) v/norm(v);
cosAngleBetween = @(a,b,c) unitVector(a-b).' * unitVector(c-b);
cosTwiceTheta = cosAngleBetween(C,B,D);
if isa(cosTwiceTheta, 'sym')
cosTwiceTheta = simplify(cosTwiceTheta);
end
theta = acos(cosTwiceTheta) / 2
radToDeg = @(rads) (rads * 180 / pi);
thetaVal = radToDeg(theta)
try
thetaVal = double(thetaVal)
end
% For the MATLAB parameters alpha=beta=0.33, we get theta = 18.263 degrees.

View File

@@ -0,0 +1,222 @@
function makeLatexReport(status, output)
% generate a LaTeX report
%
%
if ~exist('output','var')
output = m2troot('test','output','current');
end
% first, initialize the tex output
SM = StreamMaker();
stream = SM.make(fullfile(output, 'acid.tex'), 'w');
texfile_init(stream);
printFigures(stream, status);
printSummaryTable(stream, status);
printErrorMessages(stream, status);
printEnvironmentInfo(stream, status);
texfile_finish(stream);
end
% =========================================================================
function texfile_init(stream)
stream.print(['\\documentclass[landscape]{scrartcl}\n' , ...
'\\pdfminorversion=6\n\n' , ...
'\\usepackage{amsmath} %% required for $\\text{xyz}$\n\n', ...
'\\usepackage{hyperref}\n' , ...
'\\usepackage{graphicx}\n' , ...
'\\usepackage{epstopdf}\n' , ...
'\\usepackage{tikz}\n' , ...
'\\usetikzlibrary{plotmarks}\n\n' , ...
'\\usepackage{pgfplots}\n' , ...
'\\pgfplotsset{compat=newest}\n\n' , ...
'\\usepackage[margin=0.5in]{geometry}\n' , ...
'\\newlength\\figurewidth\n' , ...
'\\setlength\\figurewidth{0.4\\textwidth}\n\n' , ...
'\\begin{document}\n\n']);
end
% =========================================================================
function texfile_finish(stream)
stream.print('\\end{document}');
end
% =========================================================================
function printFigures(stream, status)
for k = 1:length(status)
texfile_addtest(stream, status{k});
end
end
% =========================================================================
function printSummaryTable(stream, status)
texfile_tab_completion_init(stream)
for k = 1:length(status)
stat = status{k};
testNumber = stat.index;
% Break table up into pieces if it gets too long for one page
%TODO: use booktabs instead
%TODO: maybe just write a function to construct the table at once
% from a cell array (see makeTravisReport for GFM counterpart)
if ~mod(k,35)
texfile_tab_completion_finish(stream);
texfile_tab_completion_init(stream);
end
stream.print('%d & \\texttt{%s}', testNumber, name2tex(stat.function));
if stat.skip
stream.print(' & --- & skipped & ---');
else
for err = [stat.plotStage.error, ...
stat.saveStage.error, ...
stat.tikzStage.error]
if err
stream.print(' & \\textcolor{red}{failed}');
else
stream.print(' & \\textcolor{green!50!black}{passed}');
end
end
end
stream.print(' \\\\\n');
end
texfile_tab_completion_finish(stream);
end
% =========================================================================
function printErrorMessages(stream, status)
if errorHasOccurred(status)
stream.print('\\section*{Error messages}\n\\scriptsize\n');
for k = 1:length(status)
stat = status{k};
testNumber = stat.index;
if isempty(stat.plotStage.message) && ...
isempty(stat.saveStage.message) && ...
isempty(stat.tikzStage.message)
continue % No error messages for this test case
end
stream.print('\n\\subsection*{Test case %d: \\texttt{%s}}\n', testNumber, name2tex(stat.function));
print_verbatim_information(stream, 'Plot generation', stat.plotStage.message);
print_verbatim_information(stream, 'PDF generation' , stat.saveStage.message);
print_verbatim_information(stream, 'matlab2tikz' , stat.tikzStage.message);
end
stream.print('\n\\normalsize\n\n');
end
end
% =========================================================================
function printEnvironmentInfo(stream, status)
[env,versionString] = getEnvironment();
testsuites = unique(cellfun(@(s) func2str(s.testsuite) , status, ...
'UniformOutput', false));
testsuites = name2tex(m2tstrjoin(testsuites, ', '));
stream.print(['\\newpage\n',...
'\\begin{tabular}{ll}\n',...
' Suite & ' testsuites ' \\\\ \n', ...
' Created & ' datestr(now) ' \\\\ \n', ...
' OS & ' OSVersion ' \\\\ \n',...
' ' env ' & ' versionString ' \\\\ \n', ...
VersionControlIdentifier, ...
' TikZ & \\expandafter\\csname ver@tikz.sty\\endcsname \\\\ \n',...
' Pgfplots & \\expandafter\\csname ver@pgfplots.sty\\endcsname \\\\ \n',...
'\\end{tabular}\n']);
end
% =========================================================================
function print_verbatim_information(stream, title, contents)
if ~isempty(contents)
stream.print(['\\subsubsection*{%s}\n', ...
'\\begin{verbatim}\n%s\\end{verbatim}\n'], ...
title, contents);
end
end
% =========================================================================
function texfile_addtest(stream, status)
% Actually add the piece of LaTeX code that'll later be used to display
% the given test.
if ~status.skip
ref_error = status.plotStage.error;
gen_error = status.tikzStage.error;
ref_file = status.saveStage.texReference;
gen_file = status.tikzStage.pdfFile;
stream.print(...
['\\begin{figure}\n' , ...
' \\centering\n' , ...
' \\begin{tabular}{cc}\n' , ...
' %s & %s \\\\\n' , ...
' reference rendering & generated\n' , ...
' \\end{tabular}\n' , ...
' \\caption{%s \\texttt{%s}, \\texttt{%s(%d)}.%s}\n', ...
'\\end{figure}\n' , ...
'\\clearpage\n\n'],...
include_figure(ref_error, 'includegraphics', ref_file), ...
include_figure(gen_error, 'includegraphics', gen_file), ...
status.description, ...
name2tex(status.function), name2tex(status.testsuite), status.index, ...
formatIssuesForTeX(status.issues));
end
end
% =========================================================================
function str = include_figure(errorOccured, command, filename)
if errorOccured
str = sprintf(['\\tikz{\\draw[red,thick] ', ...
'(0,0) -- (\\figurewidth,\\figurewidth) ', ...
'(0,\\figurewidth) -- (\\figurewidth,0);}']);
else
switch command
case 'includegraphics'
strFormat = '\\includegraphics[width=\\figurewidth]{%s}';
case 'input'
strFormat = '\\input{%s}';
otherwise
error('Matlab2tikz_acidtest:UnknownFigureCommand', ...
'Unknown figure command "%s"', command);
end
str = sprintf(strFormat, filename);
end
end
% =========================================================================
function texfile_tab_completion_init(stream)
stream.print(['\\clearpage\n\n' , ...
'\\begin{table}\n' , ...
'\\centering\n' , ...
'\\caption{Test case completion summary}\n' , ...
'\\begin{tabular}{rlccc}\n' , ...
'No. & Test case & Plot & PDF & TikZ \\\\\n' , ...
'\\hline\n']);
end
% =========================================================================
function texfile_tab_completion_finish(stream)
stream.print( ['\\end{tabular}\n' , ...
'\\end{table}\n\n' ]);
end
% =========================================================================
function texName = name2tex(matlabIdentifier)
% convert a MATLAB identifier/function handle to a TeX string
if isa(matlabIdentifier, 'function_handle')
matlabIdentifier = func2str(matlabIdentifier);
end
texName = strrep(matlabIdentifier, '_', '\_');
end
% =========================================================================
function str = formatIssuesForTeX(issues)
% make links to GitHub issues for the LaTeX output
issues = issues(:)';
if isempty(issues)
str = '';
return
end
BASEURL = 'https://github.com/matlab2tikz/matlab2tikz/issues/';
SEPARATOR = sprintf(' \n');
strs = arrayfun(@(n) sprintf(['\\href{' BASEURL '%d}{\\#%d}'], n,n), issues, ...
'UniformOutput', false);
strs = [strs; repmat({SEPARATOR}, 1, numel(strs))];
str = sprintf('{\\color{blue} \\texttt{%s}}', [strs{:}]);
end
% ==============================================================================

View File

@@ -0,0 +1,74 @@
function makeTapReport(status, varargin)
% Makes a Test Anything Protocol report
%
% This function produces a testing report of HEADLESS tests for
% display on Jenkins (or any other TAP-compatible system)
%
% MAKETAPREPORT(status) produces the report from the `status` output of
% `testHeadless`.
%
% MAKETAPREPORT(status, 'stream', FID, ...) changes the filestream to use
% to output the report to. (Default: 1 (stdout)).
%
% TAP Specification: https://testanything.org
%
% See also: testHeadless, makeTravisReport, makeLatexReport
%% Parse input arguments
SM = StreamMaker();
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'status', @iscell);
ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
ipp = ipp.parse(ipp, status, varargin{:});
arg = ipp.Results;
%% Construct stream
stream = SM.make(arg.stream, 'w');
%% build report
printTAPVersion(stream);
printTAPPlan(stream, status);
for iStatus = 1:numel(status)
printTAPReport(stream, status{iStatus}, iStatus);
end
end
% ==============================================================================
function printTAPVersion(stream)
% prints the TAP version
stream.print('TAP version 13\n');
end
function printTAPPlan(stream, statuses)
% prints the TAP test plan
firstTest = 1;
lastTest = numel(statuses);
stream.print('%d..%d\n', firstTest, lastTest);
end
function printTAPReport(stream, status, testNum)
% prints a TAP test case report
message = status.function;
if hasTestFailed(status)
result = 'not ok';
else
result = 'ok';
end
directives = getTAPDirectives(status);
stream.print('%s %d %s %s\n', result, testNum, message, directives);
%TODO: we can provide more information on the failure using YAML syntax
end
function directives = getTAPDirectives(status)
% add TAP directive (a todo or skip) to the test directives
directives = {};
if status.skip
directives{end+1} = '# SKIP skipped';
end
if status.unreliable
directives{end+1} = '# TODO unreliable';
end
directives = strtrim(m2tstrjoin(directives, ' '));
end
% ==============================================================================

View File

@@ -0,0 +1,360 @@
function [nErrors] = makeTravisReport(status, varargin)
% Makes a readable report for Travis/Github of test results
%
% This function produces a testing report of HEADLESS tests for
% display on GitHub and Travis.
%
% MAKETRAVISREPORT(status) produces the report from the `status` output of
% `testHeadless`.
%
% MAKETRAVISREPORT(status, 'stream', FID, ...) changes the filestream to use
% to output the report to. (Default: 1 (stdout)).
%
% MAKETRAVISREPORT(status, 'length', CHAR, ...) changes the report length.
% A few values are possible that cover different aspects in less/more detail.
% - 'default': all unreliable tests, failed & skipped tests and summary
% - 'short' : only show the brief summary
% - 'long' : all tests + summary
%
% See also: testHeadless, makeLatexReport
SM = StreamMaker();
%% Parse input arguments
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'status', @iscell);
ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
ipp = ipp.addParamValue(ipp, 'length', 'default', @isReportLength);
ipp = ipp.parse(ipp, status, varargin{:});
arg = ipp.Results;
arg.length = lower(arg.length);
stream = SM.make(arg.stream, 'w');
%% transform status data into groups
S = splitStatuses(status);
%% build report
stream.print(gfmHeader(describeEnvironment));
reportUnreliableTests(stream, arg, S);
reportReliableTests(stream, arg, S);
displayTestSummary(stream, S);
%% set output arguments if needed
if nargout >= 1
nErrors = countNumberOfErrors(S.reliable);
end
end
% == INPUT VALIDATOR FUNCTIONS =================================================
function bool = isReportLength(val)
% validates the report length
bool = ismember(lower(val), {'default','short','long'});
end
% == GITHUB-FLAVORED MARKDOWN FUNCTIONS ========================================
function str = gfmTable(data, header, alignment)
% Construct a Github-flavored Markdown table
%
% Arguments:
% - data: nRows x nCols cell array that represents the data
% - header: cell array with the (nCol) column headers
% - alignment: alignment specification per column
% * 'l': left-aligned (default)
% * 'c': centered
% * 'r': right-aligned
% When not enough entries are specified, the specification is repeated
% cyclically.
%
% Output: table as a string
%
% See https://help.github.com/articles/github-flavored-markdown/#tables
% input argument validation and normalization
nCols = size(data, 2);
if ~exist('alignment','var') || isempty(alignment)
alignment = 'l';
end
if numel(alignment) < nCols
% repeat the alignment specifications along the columns
alignment = repmat(alignment, 1, nCols);
alignment = alignment(1:nCols);
end
% calculate the required column width
cellWidth = cellfun(@length, [header(:)' ;data]);
columnWidth = max(max(cellWidth, [], 1),3); % use at least 3 places
% prepare the table format
COLSEP = '|'; ROWSEP = sprintf('\n');
rowformat = [COLSEP sprintf([' %%%ds ' COLSEP], columnWidth) ROWSEP];
alignmentRow = formatAlignment(alignment, columnWidth);
% actually print the table
fullTable = [header; alignmentRow; data];
strs = cell(size(fullTable,1), 1);
for iRow = 1:numel(strs)
thisRow = fullTable(iRow,:);
%TODO: maybe preprocess thisRow with strjust first
strs{iRow} = sprintf(rowformat, thisRow{:});
end
str = [strs{:}];
%---------------------------------------------------------------------------
function alignRow = formatAlignment(alignment, columnWidth)
% Construct a row of dashes to specify the alignment of each column
% See https://help.github.com/articles/github-flavored-markdown/#tables
DASH = '-'; COLON = ':';
N = numel(columnWidth);
alignRow = arrayfun(@(w) repmat(DASH, 1, w), columnWidth, ...
'UniformOutput', false);
for iColumn = 1:N
thisAlign = alignment(iColumn);
thisSpec = alignRow{iColumn};
switch lower(thisAlign)
case 'l'
thisSpec(1) = COLON;
case 'r'
thisSpec(end) = COLON;
case 'c'
thisSpec([1 end]) = COLON;
otherwise
error('gfmTable:BadAlignment','Unknown alignment "%s"',...
thisAlign);
end
alignRow{iColumn} = thisSpec;
end
end
end
function str = gfmCode(str, inline, language)
% Construct a GFM code fragment
%
% Arguments:
% - str: code to be displayed
% - inline: - true -> formats inline
% - false -> formats as code block
% - [] -> automatic mode (default): picks one of the above
% - language: which language the code is (enforces a code block)
%
% Output: GFM formatted string
%
% See https://help.github.com/articles/github-flavored-markdown
if ~exist('inline','var')
inline = [];
end
if ~exist('language','var') || isempty(language)
language = '';
else
inline = false; % highlighting is not supported for inline code
end
if isempty(inline)
inline = isempty(strfind(str, sprintf('\n')));
end
if inline
prefix = '`';
postfix = '`';
else
prefix = sprintf('\n```%s\n', language);
postfix = sprintf('\n```\n');
if str(end) == sprintf('\n')
postfix = postfix(2:end); % remove extra endline
end
end
str = sprintf('%s%s%s', prefix, str, postfix);
end
function str = gfmHeader(str, level)
% Constructs a GFM/Markdown header
if ~exist('level','var')
level = 1;
end
str = sprintf('\n%s %s\n', repmat('#', 1, level), str);
end
function symbols = githubEmoji()
% defines the emojis to signal the test result
symbols = struct('pass', ':white_check_mark:', ...
'fail', ':heavy_exclamation_mark:', ...
'skip', ':grey_question:');
end
% ==============================================================================
function S = splitStatuses(status)
% splits a cell array of statuses into a struct of cell arrays
% of statuses according to their value of "skip", "reliable" and whether
% an error has occured.
% See also: splitUnreliableTests, splitPassFailSkippedTests
S = struct('all', {status}); % beware of cell array assignment to structs!
[S.reliable, S.unreliable] = splitUnreliableTests(status);
[S.passR, S.failR, S.skipR] = splitPassFailSkippedTests(S.reliable);
[S.passU, S.failU, S.skipU] = splitPassFailSkippedTests(S.unreliable);
end
% ==============================================================================
function [short, long] = describeEnvironment()
% describes the environment in a short and long format
[env, ver] = getEnvironment;
[dummy, VCID] = VersionControlIdentifier(); %#ok
if ~isempty(VCID)
VCID = [' commit ' VCID(1:10)];
end
OS = OSVersion;
short = sprintf('%s %s (%s)', env, ver, OS, VCID);
long = sprintf('Test results for m2t%s running with %s %s on %s.', ...
VCID, env, ver, OS);
end
% ==============================================================================
function reportUnreliableTests(stream, arg, S)
% report on the unreliable tests
if ~isempty(S.unreliable) && ~strcmpi(arg.length, 'short')
stream.print(gfmHeader('Unreliable tests',2));
stream.print('These do not cause the build to fail.\n\n');
displayTestResults(stream, S.unreliable);
end
end
function reportReliableTests(stream, arg, S)
% report on the reliable tests
switch arg.length
case 'long'
tests = S.reliable;
message = '';
case 'default'
tests = [S.failR; S.skipR];
message = 'Passing tests are not shown (only failed and skipped tests).\n\n';
case 'short'
return; % don't show this part
end
stream.print(gfmHeader('Reliable tests',2));
stream.print('Only the reliable tests determine the build outcome.\n');
stream.print(message);
displayTestResults(stream, tests);
end
% ==============================================================================
function displayTestResults(stream, status)
% display a table of specific test outcomes
headers = {'Testcase', 'Name', 'OK', 'Status'};
data = cell(numel(status), numel(headers));
symbols = githubEmoji;
for iTest = 1:numel(status)
data(iTest,:) = fillTestResultRow(status{iTest}, symbols);
end
str = gfmTable(data, headers, 'llcl');
stream.print('%s', str);
end
function row = fillTestResultRow(oneStatus, symbol)
% format the status of a single test for the summary table
testNumber = oneStatus.index;
testSuite = func2str(oneStatus.testsuite);
summary = '';
if oneStatus.skip
summary = 'SKIPPED';
passOrFail = symbol.skip;
else
stages = getStagesFromStatus(oneStatus);
for jStage = 1:numel(stages)
thisStage = oneStatus.(stages{jStage});
if ~thisStage.error
continue;
end
stageName = strrep(stages{jStage},'Stage','');
switch stageName
case 'plot'
summary = sprintf('%s plot failed', summary);
case 'tikz'
summary = sprintf('%s m2t failed', summary);
case 'hash'
summary = sprintf('new hash %32s != expected (%32s) %s', ...
thisStage.found, thisStage.expected, summary);
otherwise
summary = sprintf('%s %s FAILED', summary, thisStage);
end
end
if isempty(summary)
passOrFail = symbol.pass;
else
passOrFail = symbol.fail;
end
summary = strtrim(summary);
end
row = { gfmCode(sprintf('%s(%d)', testSuite, testNumber)), ...
gfmCode(oneStatus.function), ...
passOrFail, ...
summary};
end
% ==============================================================================
function displayTestSummary(stream, S)
% display a table of # of failed/passed/skipped tests vs (un)reliable
% compute number of cases per category
reliableSummary = cellfun(@numel, {S.passR, S.failR, S.skipR});
unreliableSummary = cellfun(@numel, {S.passU, S.failU, S.skipU});
% make summary table + calculate totals
summary = [unreliableSummary numel(S.unreliable);
reliableSummary numel(S.reliable);
reliableSummary+unreliableSummary numel(S.all)];
% put results into cell array with proper layout
summary = arrayfun(@(v) sprintf('%d',v), summary, 'UniformOutput', false);
table = repmat({''}, 3, 5);
header = {'','Pass','Fail','Skip','Total'};
table(:,1) = {'Unreliable','Reliable','Total'};
table(:,2:end) = summary;
% print table
[envShort, envDescription] = describeEnvironment(); %#ok
stream.print(gfmHeader('Test summary', 2));
stream.print('%s\n', envDescription);
stream.print('%s\n', gfmCode(generateCode(S),false,'matlab'));
stream.print(gfmTable(table, header, 'lrrrr'));
% print overall outcome
symbol = githubEmoji;
nErrors = numel(S.failR);
if nErrors == 0
stream.print('\nBuild passes. %s\n', symbol.pass);
else
stream.print('\nBuild fails with %d errors. %s\n', nErrors, symbol.fail);
end
end
function code = generateCode(S)
% generates some MATLAB code to easily replicate the results
code = sprintf('%s = %s;\n', ...
'suite', ['@' func2str(S.all{1}.testsuite)], ...
'alltests', testNumbers(S.all), ...
'reliable', testNumbers(S.reliable), ...
'unreliable', testNumbers(S.unreliable), ...
'failReliable', testNumbers(S.failR), ...
'passUnreliable', testNumbers(S.passU), ...
'skipped', testNumbers([S.skipR; S.skipU]));
% --------------------------------------------------------------------------
function str = testNumbers(status)
str = intelligentVector( cellfun(@(s) s.index, status) );
end
end
function str = intelligentVector(numbers)
% Produce a string that is an intelligent vector notation of its arguments
% e.g. when numbers = [ 1 2 3 4 6 7 8 9 ], it should return '[ 1:4 6:9 ]'
% The order in the vector is not retained!
if isempty(numbers)
str = '[]';
else
numbers = sort(numbers(:).');
delta = diff([numbers(1)-1 numbers]);
% place virtual bounds at the first element and beyond the last one
bounds = [1 find(delta~=1) numel(numbers)+1];
idx = 1:(numel(bounds)-1); % start index of each segment
start = numbers(bounds(idx ) );
stop = numbers(bounds(idx+1)-1);
parts = arrayfun(@formatRange, start, stop, 'UniformOutput', false);
str = sprintf('[%s]', strtrim(sprintf('%s ', parts{:})));
end
end
function str = formatRange(start, stop)
% format a range [start:stop] of integers in MATLAB syntax
if start==stop
str = sprintf('%d',start);
else
str = sprintf('%d:%d',start, stop);
end
end
% ==============================================================================

4
Libs/mat2tikz/test/output/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# This is a directory for testing output. Nothing in here should ever
# be committed, except the .gitignore to enforce all of this.
*
!.gitignore

View File

@@ -0,0 +1,36 @@
function [formatted, OSType, OSVersion] = OSVersion()
% determines the OS type and its (kernel) version number
if ismac
OSType = 'Mac OS';
[dummy, OSVersion] = system('sw_vers -productVersion'); %#ok
% Output like "10.10.4" for OS X Yosemite
elseif ispc
OSType = 'Windows';
[dummy, rawVersion] = system('ver'); %#ok
% Output like "Microsoft Windows [Version 6.3.9600]" for Win8.1
pattern = '(?<=Version )[0-9.]+';
OSVersion = regexpi(rawVersion, pattern, 'match', 'once');
elseif isunix
[dummy, OSType] = system('uname -s'); %#ok
% This returns the kernal name
% e.g. "Linux" on Linux, "Darwin" on Mac, "SunOS" on Solaris
[dummy, OSVersion] = system('uname -r'); %#ok
% Returns the kernel version. Many Linux distributions
% include an identifier, e.g. "4.0.7-2-ARCH" on Arch Linux
% TODO: also use `lsb_release` in Linux for distro info
else
warning('OSVersion:UnknownOS', 'Could not recognize OS.');
OSType = 'Unknown OS';
OSVersion = '';
end
EOL = sprintf('\n');
OSType = strrep(OSType, EOL, '');
OSVersion = strrep(OSVersion, EOL, '');
formatted = strtrim([OSType ' ' OSVersion]);
end

View File

@@ -0,0 +1,74 @@
function SM = StreamMaker()
% StreamMaker (Factory for fie/input/output Streams)
%
% A StreamMaker can make Stream PseudoObjects based on either
% an "fid" or "filename" (and extra arguments for `fopen`).
% The StreamMaker also contains a method `isStream` to validate whether
% the value passed is a valid stream specifier.
%
% Usage
%
% SM = StreamMaker;
%
% Stream = SM.make(fid)
% Stream = SM.make(filename, ...)
%
% This returns a PseudoObject Stream with the following properties:
% - name: (file) name of the stream
% - fid: handle (fid) of the stream
%
% and methods:
% - print: prints to the stream, i.e. fprintf
% - close: closes the stream, i.e. fclose
%
% It may also contain a field to automatically close the Stream when it goes
% out of scope.
%
SM = PseudoObject('StreamMaker', ...
'isStream', @isStream, ...
'make', @constructStream);
end
function PseudoObj = PseudoObject(T, varargin)
% construct a Pseudo-Object with type T (no other fields yet)
PseudoObj = struct('Type', T, varargin{:});
end
function bool = isStream(value)
bool = ischar(value) || ismember(value, [1,2,fopen('all')]);
%TODO: allow others kinds of streams
% Stream -> clipboard (write on close)
% Stream -> string variable
% e.g. a quick-and-dirty way would be to write the file to `tempname`
% putting a flag to read that file back upon completion.
end
function Stream = constructStream(streamSpecifier, varargin)
% this is the actual constructor of a stream
if ~isStream(streamSpecifier)
error('StreamMaker:NotAStream', 'Invalid stream specifier "%s"', ...
streamSpecifier);
end
Stream = PseudoObject('Stream');
closeAfterUse = false;
if ischar(streamSpecifier)
Stream.name = streamSpecifier;
Stream.fid = fopen(Stream.name, varargin{:});
closeAfterUse = true;
elseif isnumeric(streamSpecifier)
Stream.fid = streamSpecifier;
Stream.name = fopen(Stream.fid);
end
if Stream.fid == -1
error('Stream:InvalidStream', ...
'Unable to create stream "%s"!', streamSpecifier);
end
Stream.print = @(varargin) fprintf(Stream.fid, varargin{:});
Stream.close = @() fclose(Stream.fid);
if closeAfterUse
Stream.closeAfterUse = onCleanup(Stream.close);
end
end

View File

@@ -0,0 +1,47 @@
function [formatted,treeish] = VersionControlIdentifier()
% This function gives the (git) commit ID of matlab2tikz
%
% This assumes the standard directory structure as used by Nico's master branch:
% SOMEPATH/src/matlab2tikz.m with a .git directory in SOMEPATH.
%
% The HEAD of that repository is determined from file system information only
% by following dynamic references (e.g. ref:refs/heds/master) in branch files
% until an absolute commit hash (e.g. 1a3c9d1...) is found.
% NOTE: Packed branch references are NOT supported by this approach
MAXITER = 10; % stop following dynamic references after a while
formatted = '';
REFPREFIX = 'ref:';
isReference = @(treeish)(any(strfind(treeish, REFPREFIX)));
treeish = [REFPREFIX 'HEAD'];
try
% get the matlab2tikz directory
privateDir = fileparts(mfilename('fullpath'));
gitDir = fullfile(privateDir,'..','..','.git');
nIter = 1;
while isReference(treeish)
refName = treeish(numel(REFPREFIX)+1:end);
branchFile = fullfile(gitDir, refName);
if exist(branchFile, 'file') && nIter < MAXITER
% The FID is reused in every iteration, so `onCleanup` cannot
% be used to `fclose(fid)`. But since there is very little that
% can go wrong in a single `fscanf`, it's probably best to leave
% this part as it is for the time being.
fid = fopen(branchFile,'r');
treeish = fscanf(fid,'%s');
fclose(fid);
nIter = nIter + 1;
else % no branch file or iteration limit reached
treeish = '';
return;
end
end
catch %#ok
treeish = '';
end
if ~isempty(treeish)
formatted = [' Commit & ' treeish ' \\\\ \n'];
end
%TODO: do the formatting somewhere else!
end

View File

@@ -0,0 +1,35 @@
function hash = calculateMD5Hash(filename)
% CALCULATEMD5HASH calculate a MD5 hash of a file
%
% This functionality is built-in into Octave but uses Java in MATLAB.
switch getEnvironment
case 'Octave'
hash = md5sum(filename);
case 'MATLAB'
% There are some MD5 implementations in MATLAB, but those
% tend to be slow and licensing is unclear.
% Rolling our own implementation is unwanted, especially since this
% is a cryptographic hash, even though its security has been
% broken. Instead we make use of the Java libraries.
% Unless the "-nojvm" flag is specified, this should work well.
MD5 = java.security.MessageDigest.getInstance('MD5');
% Open the file
fid = fopen(filename, 'r');
% Make sure fid is closed
finally_close = onCleanup(@()fclose(fid));
% Faster file digest based on code by Jan Simon as in
% http://www.mathworks.com/matlabcentral/fileexchange/31272-datahash
data = fread(fid, '*uint8');
MD5.update(data);
hash = reshape(dec2hex(typecast(MD5.digest(),'uint8')).', 1, 32);
end
hash = lower(hash);
end

View File

@@ -0,0 +1,19 @@
function cleanFiles(cleanBefore)
% clean output files in ./tex using make
%FIXME: this file appears to be unused (but it is useful)
%FIXME: adapt this file to take the output directory into account
if cleanBefore && exist(fullfile('tex','Makefile'),'file')
fprintf(1, 'Cleaning output files...\n');
cwd = pwd;
try
cd('tex');
[exitCode, output] = system('make distclean');
fprintf(1,'%s\n', output);
assert(exitCode==0, 'Exit code 0 means correct execution');
catch
% This might happen when make is not present
fprintf(2, '\tNot completed succesfully\n\n');
end
cd(cwd);
end
end

View File

@@ -0,0 +1,4 @@
function nErrors = countNumberOfErrors(status)
% counts the number of errors in a status cell array
nErrors = sum(hasTestFailed(status));
end

View File

@@ -0,0 +1,4 @@
function stage = emptyStage()
% constructs an empty (workflow) stage struct
stage = struct('message', '', 'error' , false);
end

View File

@@ -0,0 +1,24 @@
function defaultStatus = emptyStatus(testsuite, testNumber)
% constructs an empty status struct
defaultStatus = struct(...
'function', '', ...
'description', '',...
'testsuite', testsuite ,...
'index', testNumber, ...
'issues', [],...
'unreliable', false, ...
'skip', false, ... % skipped this test?
'closeall', false, ... % call close all after?
'extraOptions', {cell(0)}, ...
'extraCleanfigureOptions',{cell(0)}, ...
'plotStage', emptyStage(), ...
'saveStage', emptyStage(), ...
'tikzStage', emptyStage(), ...
'hashStage', emptyStage() ...
);
% for reliable tests explicitly define width and height, see #659
% TODO: Remove explicitly setting this option.
% After #641 is merged, this might be not needed anyhow.
defaultStatus.extraCleanfigureOptions = {'targetResolution', [1000,500]};
end

View File

@@ -0,0 +1,41 @@
function [stage, errorHasOccurred] = errorHandler(e)
% common error handler code: save and print to console
errorHasOccurred = true;
stage = emptyStage();
stage.message = format_error_message(e);
stage.error = errorHasOccurred;
disp_error_message(stage.message);
end
% ==============================================================================
function msg = format_error_message(e)
msg = '';
if ~isempty(e.message)
msg = sprintf('%serror: %s\n', msg, e.message);
end
if ~isempty(e.identifier)
if strfind(lower(e.identifier),'testmatlab2tikz:')
% When "errors" occur in the test framework, i.e. a hash mismatch
% or no hash provided, there is no need to be very verbose.
% So we don't return the msgid and the stack trace in those cases!
return % only return the message
end
msg = sprintf('%serror: %s\n', msg, e.identifier);
end
if ~isempty(e.stack)
msg = sprintf('%serror: called from:\n', msg);
for ee = e.stack(:)'
msg = sprintf('%serror: %s at line %d, in function %s\n', ...
msg, ee.file, ee.line, ee.name);
end
end
end
% ==============================================================================
function disp_error_message(msg)
stderr = 2;
% The error message should not contain any more escape sequences and
% hence can be output literally to stderr.
fprintf(stderr, '%s', msg);
end
% ==============================================================================

View File

@@ -0,0 +1,16 @@
function errorOccurred = errorHasOccurred(status)
% determines whether an error has occurred from a status struct OR cell array
% of status structs
errorOccurred = false;
if iscell(status)
for iStatus = 1:numel(status)
errorOccurred = errorOccurred || errorHasOccurred(status{iStatus});
end
else
stages = getStagesFromStatus(status);
for iStage = 1:numel(stages)
thisStage = status.(stages{iStage});
errorOccurred = errorOccurred || thisStage.error;
end
end
end

View File

@@ -0,0 +1,34 @@
function [status] = execute_hash_stage(status, ipp)
% test stage: check recorded hash checksum
calculated = '';
expected = '';
try
expected = getReferenceHash(status, ipp);
calculated = calculateMD5Hash(status.tikzStage.texFile);
% do the actual check
if ~strcmpi(expected, calculated)
% throw an error to signal the testing framework
error('testMatlab2tikz:HashMismatch', ...
'The hash "%s" does not match the reference hash "%s"', ...
calculated, expected);
end
catch %#ok
e = lasterror('reset'); %#ok
[status.hashStage] = errorHandler(e);
end
status.hashStage.expected = expected;
status.hashStage.found = calculated;
end
% ==============================================================================
function hash = getReferenceHash(status, ipp)
% retrieves a reference hash from a hash table
% WARNING: do not make `hashTable` persistent, since this is slower
hashTable = loadHashTable(ipp.Results.testsuite);
if isfield(hashTable.contents, status.function)
hash = hashTable.contents.(status.function);
else
hash = '';
end
end

View File

@@ -0,0 +1,45 @@
function [status] = execute_plot_stage(defaultStatus, ipp)
% plot a test figure
testsuite = ipp.Results.testsuite;
testNumber = defaultStatus.index;
% open a window
fig_handle = figure('visible',ipp.Results.figureVisible);
errorHasOccurred = false;
% plot the figure
try
status = testsuite(testNumber);
catch %#ok
e = lasterror('reset'); %#ok
status.description = '\textcolor{red}{Error during plot generation.}';
[status.plotStage, errorHasOccurred] = errorHandler(e);
% Automaticall mark the test as unreliable
%
% Since metadata is not set in this case, also stat.unreliable is
% not returned. So ideally, we should
% FIXME: implement #484 to get access to the meta data
% but we can work around this issue by forcefully setting that value.
% The rationale for setting this to true:
% - the plot part is not the main task of M2T
% (so breaking a single test is less severe in this case),
% - if the plotting fails, the test is not really reliable anyway,
% - this allows to get full green on Travis.
status.unreliable = true;
end
status = fillStruct(status, defaultStatus);
if isempty(status.function)
allFuncs = testsuite(0);
status.function = func2str(allFuncs{testNumber});
end
status.plotStage.fig_handle = fig_handle;
if status.skip || errorHasOccurred
close(fig_handle);
end
end

View File

@@ -0,0 +1,63 @@
function [status] = execute_save_stage(status, ipp)
% save stage: saves the figure to EPS/PDF depending on env
testNumber = status.index;
basepath = fullfile(ipp.Results.output,'data','reference');
reference_eps = fullfile(basepath, sprintf('test%d-reference.eps', testNumber));
reference_pdf = fullfile(basepath, sprintf('test%d-reference.pdf', testNumber));
% the reference below is for inclusion in LaTeX! Use UNIX conventions!
reference_fig = sprintf('data/reference/test%d-reference', testNumber);
% Save reference output as PDF
try
switch getEnvironment
case 'MATLAB'
% MATLAB does not generate properly cropped PDF files.
% So, we generate EPS files that are converted later on.
print(gcf, '-depsc2', reference_eps);
fixLineEndingsInWindows(reference_eps);
case 'Octave'
% In Octave, figures are properly cropped when using print().
print(reference_pdf, '-dpdf', '-S415,311', '-r150');
pause(1.0)
otherwise
error('matlab2tikz:UnknownEnvironment', ...
'Unknown environment. Need MATLAB(R) or GNU Octave.')
end
catch %#ok
e = lasterror('reset'); %#ok
[status.saveStage] = errorHandler(e);
end
status.saveStage.epsFile = reference_eps;
status.saveStage.pdfFile = reference_pdf;
status.saveStage.texReference = reference_fig;
end
% ==============================================================================
function fixLineEndingsInWindows(filename)
% On R2014b Win, line endings in .eps are Unix style (LF) instead of Windows
% style (CR+LF). This causes problems in the MikTeX `epstopdf` for some files
% as dicussed in:
% * https://github.com/matlab2tikz/matlab2tikz/issues/370
% * http://tex.stackexchange.com/questions/208179
if ispc
fid = fopen(filename,'r+');
finally_fclose_fid = onCleanup(@() fclose(fid));
testline = fgets(fid);
CRLF = sprintf('\r\n');
endOfLine = testline(end-1:end);
if ~strcmpi(endOfLine, CRLF)
endOfLine = testline(end); % probably an LF
% Rewind, read the whole
fseek(fid,0,'bof');
str = fread(fid,'*char')';
% Replace, overwrite and close
str = strrep(str, endOfLine, CRLF);
fseek(fid,0,'bof');
fprintf(fid,'%s',str);
end
end
end

View File

@@ -0,0 +1,43 @@
function [status] = execute_tikz_stage(status, ipp)
% test stage: TikZ file generation
testNumber = status.index;
datapath = fullfile(ipp.Results.output,'data','converted');
gen_tex = fullfile(datapath, sprintf('test%d-converted.tex', testNumber));
% the value below is for inclusion into LaTeX report! Use UNIX convention.
gen_pdf = sprintf('data/converted/test%d-converted.pdf', testNumber);
cleanfigure_time = NaN;
m2t_time = NaN;
% now, test matlab2tikz
try
%TODO: remove this once text removal has been removed
oldWarn = warning('off','cleanfigure:textRemoval');
cleanfigure_time = tic;
cleanfigure(status.extraCleanfigureOptions{:});
cleanfigure_time = toc(cleanfigure_time);
warning(oldWarn);
m2t_time = tic;
matlab2tikz('filename', gen_tex, ...
'showInfo', false, ...
'checkForUpdates', false, ...
'dataPath', datapath, ...
'standalone', true, ...
ipp.Results.extraOptions{:}, ...
status.extraOptions{:} ...
);
m2t_time = toc(m2t_time);
catch %#ok
e = lasterror('reset'); %#ok
% Remove (corrupted) output file. This is necessary to avoid that the
% Makefile tries to compile it and fails.
delete(gen_tex)
[status.tikzStage] = errorHandler(e);
end
status.tikzStage.texFile = gen_tex;
status.tikzStage.pdfFile = gen_pdf;
status.tikzStage.m2t_time = m2t_time;
status.tikzStage.cleanfigure_time = cleanfigure_time;
end

View File

@@ -0,0 +1,15 @@
function [status] = execute_type_stage(status, ipp)
try
filename = status.tikzStage.texFile;
stream = 1; % stdout
if errorHasOccurred(status) && exist(filename, 'file')
shortname = strrep(filename, m2troot, '$(M2TROOT)');
fprintf(stream, '\n%%%%%%%% BEGIN FILE "%s" %%%%%%%%\n', shortname);
type(filename);
fprintf(stream, '\n%%%%%%%% END FILE "%s" %%%%%%%%\n', shortname);
end
catch
e = lasterror('reset');
[status.typeStage] = errorHandler(e);
end
end

View File

@@ -0,0 +1,10 @@
function [status] = fillStruct(status, defaultStatus)
% fills non-existant fields of |data| with those of |defaultData|
fields = fieldnames(defaultStatus);
for iField = 1:numel(fields)
field = fields{iField};
if ~isfield(status,field)
status.(field) = defaultStatus.(field);
end
end
end

View File

@@ -0,0 +1,25 @@
function [env, versionString] = getEnvironment()
% Determine environment (Octave, MATLAB) and version string
% TODO: Unify private `getEnvironment` functions
persistent cache
if isempty(cache)
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
if isOctave
env = 'Octave';
versionString = OCTAVE_VERSION;
else
env = 'MATLAB';
vData = ver(env);
versionString = vData.Version;
end
% store in cache
cache.env = env;
cache.versionString = versionString;
else
env = cache.env;
versionString = cache.versionString;
end
end

View File

@@ -0,0 +1,5 @@
function stages = getStagesFromStatus(status)
% retrieves the different (names of) stages of a status struct
fields = fieldnames(status);
stages = fields(cellfun(@(f) ~isempty(strfind(f,'Stage')), fields));
end

View File

@@ -0,0 +1,13 @@
function bool = hasTestFailed(status)
% returns true when the test has failed
if iscell(status) % allow for vectorization of the call
bool = cellfun(@hasTestFailed, status, 'UniformOutput', true);
else
stages = getStagesFromStatus(status);
bool = false;
for jStage = 1:numel(stages)
bool = bool || status.(stages{jStage}).error;
end
end
end

View File

@@ -0,0 +1,55 @@
function filename = hashTableName(suite)
% determines the file name of a hash table
%
% The MD5 file is assumed to be in the same directory as the test suite.
% It has a file name "$SUITE.$ENV.$VER.md5"
% where the following fields are filled:
% $ENV: the environment (either "MATLAB" or "Octave")
% $VER: the version (e.g. "3.8.0" for Octave, "8.3" for MATLAB 2014a)
% $SUITE: the name (and path) of the test suite
%
% For the $VER-part, a fall-back mechanism is present that prefers the exact
% version but will use the closest available file if such file does not
% exist.
[pathstr,name, ext] = fileparts(which(func2str(suite)));
[env, version] = getEnvironment();
ext = sprintf('.%s.%s.md5', env, version);
relFilename = [name ext];
filename = fullfile(pathstr, relFilename);
if ~exist(filename,'file')
% To avoid having to create a file for each release of the environment,
% also other versions are tried. The file for different releases are checked
% in the following order:
% 1. the currently running version (handled above!)
% 2. the newest older version (e.g. use R2014b's file in R2015a)
% 3. the oldest newer version (e.g. use R2014a's file in R2013a)
pattern = sprintf('%s.%s.*.md5', name, env);
candidates = dir(fullfile(pathstr, pattern));
% We just need the file names.
filenames = arrayfun(@(c)c.name, candidates, 'UniformOutput', false);
% Add the expected version to the results, and sort the names by
% version (this is the same as alphabetically).
filenames = sort([filenames; {relFilename}]);
nFiles = numel(filenames);
iCurrent = find(ismember(filenames, relFilename));
% determine the fall-back candidates:
iNewestOlder = iCurrent - 1;
iOldestNewer = iCurrent + 1;
inRange = @(idx)(idx <= nFiles && idx >= 1);
if inRange(iNewestOlder)
% use the newest older version
relFilename = filenames{iNewestOlder};
elseif inRange(iOldestNewer)
% use the oldest newer version
relFilename = filenames{iOldestNewer};
else
% use the exact version anyhow
end
filename = fullfile(pathstr, relFilename);
end
end

View File

@@ -0,0 +1,99 @@
function [orig] = initializeGlobalState()
% Initialize global state. Set working directory and various properties of
% the graphical root to ensure reliable output of the ACID testsuite.
% See #542 and #552
%
% 1. Working directory
% 2. Bring get(0,'Default') in line with get(0,'Factory')
% 3. Set specific properties, required by matlab2tikz
fprintf('Initialize global state...\n');
orig = struct();
%--- Extract user defined default properties and set factory state
default = get(0,'Default');
factory = get(0,'Factory');
f = fieldnames(default); % fields of user's default state
for i = 1:length(f)
factory_property_name = strrep(f{i},'default','factory');
factory_property_value = factory.(factory_property_name);
orig.(f{i}).val = ...
swapPropertyState(0, f{i}, factory_property_value);
end
%--- Define desired global state properties
% defaultAxesColorOrder: on HG1 'default' and 'factory' differ and
% HG1 differs from HG2. Consequently use HG2 colors (the new standard).
new.defaultAxesColorOrder.val = [0.000 0.447 0.741; ...
0.850 0.325 0.098; ...
0.929 0.694 0.125; ...
0.494 0.184 0.556; ...
0.466 0.674 0.188; ...
0.301 0.745 0.933; ...
0.635 0.0780 0.184];
new.defaultAxesColorOrder.ignore= false;
% defaultFigurePosition: width and height influence cleanfigure() and
% the number/location of axis ticks
new.defaultFigurePosition.val = [300,200,560,420];
new.defaultFigurePosition.ignore= false;
% ScreenPixelsPerInch: TODO: determine, if necessary
% (probably needed for new line simplification algorithm)
% not possible in octave
new.ScreenPixelsPerInch.val = 96;
new.ScreenPixelsPerInch.ignore = strcmpi(getEnvironment,'octave');
% MATLAB's factory values differ from their default values of a clean
% MATLAB installation (observed on R2014a, Linux)
new.defaultAxesColor.val = [1 1 1];
new.defaultAxesColor.ignore = false;
new.defaultLineColor.val = [0 0 0];
new.defaultLineColor.ignore = false;
new.defaultTextColor.val = [0 0 0];
new.defaultTextColor.ignore = false;
new.defaultAxesXColor.val = [0 0 0];
new.defaultAxesXColor.ignore = false;
new.defaultAxesYColor.val = [0 0 0];
new.defaultAxesYColor.ignore = false;
new.defaultAxesZColor.val = [0 0 0];
new.defaultAxesZColor.ignore = false;
new.defaultFigureColor.val = [0.8 0.8 0.8];
new.defaultFigureColor.ignore = false;
new.defaultPatchEdgeColor.val = [0 0 0];
new.defaultPatchEdgeColor.ignore = false;
new.defaultPatchFaceColor.val = [0 0 0];
new.defaultPatchFaceColor.ignore = false;
new.defaultFigurePaperType.val = 'A4';
new.defaultFigurePaperType.ignore = false;
new.defaultFigurePaperSize.val = [20.9840 29.6774];
new.defaultFigurePaperSize.ignore = false;
new.defaultFigurePaperUnits.val = 'centimeters';
new.defaultFigurePaperUnits.ignore = false;
%--- Extract relevant properties and set desired state
f = fieldnames(new); % fields of new state
for i = 1:length(f)
% ignore property on specified environments
if ~new.(f{i}).ignore
val = swapPropertyState(0, f{i}, new.(f{i}).val);
% store original value only, if not set by user's defaults
if ~isfield(orig,f{i})
orig.(f{i}).val = val;
end
end
end
end
% =========================================================================
function old = swapPropertyState(h, property, new)
% read current property of graphical object
% set new value, if not empty
if nargin < 3, new = []; end
old = get(h, property);
if ~isempty(new)
set(h, property, new);
end
end

View File

@@ -0,0 +1,19 @@
function hashTable = loadHashTable(suite)
% loads a reference hash table from disk
hashTable.suite = suite;
hashTable.contents = struct();
filename = hashTableName(suite);
if exist(filename, 'file')
fid = fopen(filename, 'r');
finally_fclose_fid = onCleanup(@() fclose(fid));
data = textscan(fid, '%s : %s');
if ~isempty(data) && ~all(cellfun(@isempty, data))
functions = cellfun(@strtrim, data{1},'UniformOutput', false);
hashes = cellfun(@strtrim, data{2},'UniformOutput', false);
for iFunc = 1:numel(functions)
hashTable.contents.(functions{iFunc}) = hashes{iFunc};
end
end
end
end

View File

@@ -0,0 +1,30 @@
function rootpath = m2troot(varargin)
% M2TROOT produces paths inside the matlab2tikz repository
%
% Usage:
% There are two ways to call this function, the base syntax is:
%
% * rootpath = m2troot()
%
% where |rootpath| points towards the root of the repository.
%
% The other syntax:
%
% * path = m2troot(...)
%
% is equivalent to |fullfile(m2troot, ...)| and as such allows to
% easily produce a path to any file within the repository.
m2t = which('matlab2tikz');
if isempty(m2t)
error('M2TRoot:NotFound', 'Matlab2tikz was not found on the PATH!')
end
[srcpath] = fileparts(m2t); % this should be $(m2troot)/src
[rootpath, srcdir] = fileparts(srcpath); % this should be $(m2troot)
assert(strcmpi(srcdir,'src'));
if nargin >= 1
rootpath = fullfile(rootpath, varargin{:});
end
end

View File

@@ -0,0 +1,24 @@
function [ newstr ] = m2tstrjoin( cellstr, delimiter )
%M2TSTRJOIN This function joins a cellstr with a separator
%
% This is an alternative implementation for MATLAB's `strjoin`, since that
% one is not available before R2013a.
%
% See also: strjoin
%TODO: Unify the private `m2tstrjoin` functions
%FIXME: differs from src/private/m2tstrjoin in functionality !!!
nElem = numel(cellstr);
if nElem == 0
newstr = '';
return % m2tstrjoin({}, ...) -> ''
end
newstr = cell(2,nElem);
newstr(1,:) = reshape(cellstr, 1, nElem);
newstr(2,1:nElem-1) = {delimiter}; % put delimiters in-between the elements
newstr(2, end) = {''}; % for Octave 4 compatibility
newstr = [newstr{:}];
end

View File

@@ -0,0 +1,11 @@
function restoreGlobalState(orig)
% Restore original properties of global state.
% See #542 and #552
fprintf('Restore global state...\n');
% Restore relevant properties
state_fields = fieldnames(orig);
for i = 1:length(state_fields)
set(0, state_fields{i}, orig.(state_fields{i}).val);
end
end

View File

@@ -0,0 +1,10 @@
function [passedTests, failedTests, skippedTests] = splitPassFailSkippedTests(status)
% splits tests between passed, failed and skippedtests
skipped = cellfun(@(s) s.skip, status);
status_notSkipped = status(~skipped);
failed = hasTestFailed(status_notSkipped);
passedTests = status_notSkipped(~failed);
failedTests = status_notSkipped(failed);
skippedTests = status(skipped);
end

View File

@@ -0,0 +1,7 @@
function [reliableTests, unreliableTests] = splitUnreliableTests(status)
% splits tests between reliable and unreliable tests
knownToFail = cellfun(@(s)s.unreliable, status);
unreliableTests = status( knownToFail);
reliableTests = status(~knownToFail);
end

View File

@@ -0,0 +1,146 @@
function [status, parameters] = testMatlab2tikz(varargin)
%TESTMATLAB2TIKZ unit test driver for matlab2tikz
%
% This function should NOT be called directly by the user (or even developer).
% If you are a developer, please use some of the following functions instead:
% * `testHeadless`
% * `testGraphical`
%
% The following arguments are supported, also for the functions above.
%
% TESTMATLAB2TIKZ('testFunctionIndices', INDICES, ...) or
% TESTMATLAB2TIKZ(INDICES, ...) runs the test only for the specified
% indices. When empty, all tests are run. (Default: []).
%
% TESTMATLAB2TIKZ('extraOptions', {'name',value, ...}, ...)
% passes the cell array of options to MATLAB2TIKZ. Default: {}
%
% TESTMATLAB2TIKZ('figureVisible', LOGICAL, ...)
% plots the figure visibly during the test process. Default: false
%
% TESTMATLAB2TIKZ('testsuite', FUNCTION_HANDLE, ...)
% Determines which test suite is to be run. Default: @ACID
% A test suite is a function that takes a single integer argument, which:
% when 0: returns a cell array containing the N function handles to the tests
% when >=1 and <=N: runs the appropriate test function
% when >N: throws an error
%
% TESTMATLAB2TIKZ('output', DIRECTORY, ...)
% Sets the output directory where the output files are places.
% The default directory is $M2TROOT/test/output/current
%
% See also matlab2tikz, ACID
% In which environment are we?
env = getEnvironment();
% -----------------------------------------------------------------------
ipp = m2tInputParser;
ipp = ipp.addOptional(ipp, 'testFunctionIndices', [], @isfloat);
ipp = ipp.addParamValue(ipp, 'extraOptions', {}, @iscell);
ipp = ipp.addParamValue(ipp, 'figureVisible', false, @islogical);
ipp = ipp.addParamValue(ipp, 'actionsToExecute', @(varargin) varargin{1}, @isFunction);
ipp = ipp.addParamValue(ipp, 'testsuite', @ACID, @isFunction );
ipp = ipp.addParamValue(ipp, 'output', m2troot('test','output','current'), @ischar);
ipp = ipp.parse(ipp, varargin{:});
ipp = sanitizeInputs(ipp);
parameters = ipp.Results;
% -----------------------------------------------------------------------
if strcmpi(env, 'Octave')
if ~ipp.Results.figureVisible
% Use the gnuplot backend to work around an fltk bug, see
% <http://savannah.gnu.org/bugs/?43429>.
graphics_toolkit gnuplot
end
if ispc
% Prevent three digit exponent on Windows Octave
% See https://github.com/matlab2tikz/matlab2tikz/pull/602
setenv ('PRINTF_EXPONENT_DIGITS', '2')
end
end
% copy output template into output directory
if ~exist(ipp.Results.output,'dir')
mkdir(ipp.Results.output);
end
template = m2troot('test','template');
copyfile(fullfile(template,'*'), ipp.Results.output);
% start overall timing
elapsedTimeOverall = tic;
status = runIndicatedTests(ipp);
% print out overall timing
elapsedTimeOverall = toc(elapsedTimeOverall);
stdout = 1;
fprintf(stdout, 'overall time: %4.2fs\n\n', elapsedTimeOverall);
end
% INPUT VALIDATION =============================================================
function bool = isFunction(f)
bool = isa(f,'function_handle');
end
function ipp = sanitizeInputs(ipp)
% sanitize all input arguments
ipp = sanitizeFunctionIndices(ipp);
ipp = sanitizeFigureVisible(ipp);
end
function ipp = sanitizeFunctionIndices(ipp)
% sanitize the passed function indices to the range of the test suite
% query the number of test functions
testsuite = ipp.Results.testsuite;
n = length(testsuite(0));
if ~isempty(ipp.Results.testFunctionIndices)
indices = ipp.Results.testFunctionIndices;
% kick out the illegal stuff
I = find(indices>=1 & indices<=n);
indices = indices(I); %#ok
else
indices = 1:n;
end
ipp.Results.testFunctionIndices = indices;
end
function ipp = sanitizeFigureVisible(ipp)
% sanitizes the figure visible option from boolean to ON/OFF
if ipp.Results.figureVisible
ipp.Results.figureVisible = 'on';
else
ipp.Results.figureVisible = 'off';
end
end
% TEST RUNNER ==================================================================
function status = runIndicatedTests(ipp)
% run all indicated tests in the test suite
% cell array to accomodate different structure
indices = ipp.Results.testFunctionIndices;
testsuite = ipp.Results.testsuite;
testsuiteName = func2str(testsuite);
stdout = 1;
status = cell(length(indices), 1);
for k = 1:length(indices)
testNumber = indices(k);
fprintf(stdout, 'Executing %s test no. %d...\n', testsuiteName, indices(k));
status{k} = emptyStatus(testsuite, testNumber);
elapsedTime = tic;
status{k} = feval(ipp.Results.actionsToExecute, status{k}, ipp);
elapsedTime = toc(elapsedTime);
status{k}.elapsedTime = elapsedTime;
fprintf(stdout, '%s ', status{k}.function);
if status{k}.skip
fprintf(stdout, 'skipped (%4.2fs).\n\n', elapsedTime);
else
fprintf(stdout, 'done (%4.2fs).\n\n', elapsedTime);
end
end
end

View File

@@ -0,0 +1,38 @@
function statusAll = runMatlab2TikzTests(varargin)
%% This file runs the complete MATLAB2TIKZ test suite.
% It is mainly used for testing on a continuous integration server, but it can
% also be used on a development machine.
CI_MODE = strcmpi(getenv('CONTINUOUS_INTEGRATION'),'true') || strcmp(getenv('CI'),'true');
isJenkins = ~isempty(getenv('JENKINS_URL'));
%% Set path
addpath(fullfile(pwd,'..','src'));
addpath(fullfile(pwd,'suites'));
%% Select functions to run
suite = @ACID;
allTests = 1:numel(suite(0));
%% Prepare environment
if strcmpi(getEnvironment(), 'Octave')
% Ensure that paging is disabled
% https://www.gnu.org/software/octave/doc/interpreter/Paging-Screen-Output.html
more off
end
%% Run tests
status = testHeadless('testFunctionIndices', allTests,...
'testsuite', suite, varargin{:});
if isJenkins
makeTapReport(status, 'stream', 'results.test.tap');
makeTravisReport(status, 'stream', 'results.test.md');
end
nErrors = makeTravisReport(status);
%% Calculate exit code
if CI_MODE
exit(nErrors);
end

View File

@@ -0,0 +1,164 @@
function saveHashTable(status, varargin)
% SAVEHASHTABLE saves the references hashes for the Matlab2Tikz tests
%
% Usage:
% SAVEHASHTABLE(status)
%
% SAVEHASHTABLE(status, 'dryrun', BOOL, ...) determines whether or not to
% write the constructed hash table to file (false) or to stdout (true).
% Default: false
%
% SAVEHASHTABLE(status, 'removedTests', CHAR, ...) specifies which action to
% execute on "removed tests" (i.e. test that have a hash recorded in the file,
% but which are not present in `status`). Three values are possible:
% - 'ask' (default): Ask what to do for each such test.
% - 'remove': Remove the test from the file.
% This is appropriate if the test has been removed from the suite.
% - 'keep': Keep the test hash in the file.
% This is appropriate when the test has not executed all tests.
%
% Inputs:
% - status: output cell array of the testing functions
%
% See also: runMatlab2TikzTests, testMatlab2tikz
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'status', @iscell);
ipp = ipp.addParamValue(ipp, 'dryrun', false, @islogical);
ipp = ipp.addParamValue(ipp, 'removedTests', 'ask', @isValidAction);
ipp = ipp.parse(ipp, status, varargin{:});
%% settings
suite = status{1}.testsuite; %TODO: handle multiple test suites in a single array
filename = hashTableName(suite);
READFORMAT = '%s : %s';
WRITEFORMAT = [READFORMAT '\n'];
%% process the hash table
oldHashes = readHashesFromFile(filename);
newHashes = updateHashesFromStatus(oldHashes, status);
writeHashesToFile(filename, newHashes);
% --------------------------------------------------------------------------
function hashes = updateHashesFromStatus(hashes, status)
% update hashes from the test results in status
oldFunctions = fieldnames(hashes);
newFunctions = cellfun(@(s) s.function, status, 'UniformOutput', false);
% add hashes from all executed tests
for iFunc = 1:numel(status)
S = status{iFunc};
thisFunc = S.function;
thisHash = '';
if isfield(S.hashStage,'found')
thisHash = S.hashStage.found;
elseif S.skip
if isfield(hashes, thisFunc)
% Test skipped, but reference hash present in file
% Probably this means that the developer doesn't have access
% to a certain toolbox.
warning('SaveHashTable:CannotUpdateSkippedTest', ...
'Test "%s" was skipped. Cannot update hash!',...
thisFunc);
else
% Test skipped and reference hash absent.
% Probably the test is skipped because something is tested
% that relies on HG1/HG2/Octace-specific features and we are
% in the wrong environment for the test.
end
else
warning('SaveHashTable:NoHashFound',...
'No hash found for "%s"!', thisFunc);
end
if ~isempty(thisHash)
hashes.(thisFunc) = thisHash;
end
end
% ask what to do with tests for which we have a hash, but no test results
removedTests = setdiff(oldFunctions, newFunctions);
if ~isempty(removedTests)
fprintf(1, 'Some tests in the file were not in the build status.\n');
end
for iTest = 1:numel(removedTests)
thisTest = removedTests{iTest};
action = askActionToPerformOnRemovedTest(thisTest);
switch action
case 'remove'
% useful for test that no longer exist
fprintf(1, 'Removed hash for "%s"\n', thisTest);
hashes = rmfield(hashes, thisTest);
case 'keep'
% useful when not all tests were executed by the tester
fprintf(1, 'Kept hash for "%s"\n', thisTest);
end
end
end
function action = askActionToPerformOnRemovedTest(testName)
% ask which action to carry out on a removed test
action = lower(ipp.Results.removedTests);
while ~isActualAction(action)
query = sprintf('Keep or remove "%s"? [Kr]:', testName);
answer = strtrim(input(query,'s'));
if isempty(answer) || strcmpi(answer(1), 'K')
action = 'keep';
elseif strcmpi(answer(1), 'R')
action = 'remove';
else
action = 'ask again';
% just keep asking until we get a reasonable answer
end
end
end
function writeHashesToFile(filename, hashes)
% write hashes to a file (or stdout when dry-running)
if ~ipp.Results.dryrun
fid = fopen(filename, 'w+');
finally_fclose_fid = onCleanup(@() fclose(fid));
else
fid = 1; % Use stdout to print everything
fprintf(fid, '\n\n Output: \n\n');
end
funcNames = sort(fieldnames(hashes));
for iFunc = 1:numel(funcNames)
func = funcNames{iFunc};
fprintf(fid, WRITEFORMAT, func, hashes.(func));
end
end
function hashes = readHashesFromFile(filename)
% read hashes from a file
if exist(filename,'file')
fid = fopen(filename, 'r');
finally_fclose_fid = onCleanup(@() fclose(fid));
data = textscan(fid, READFORMAT);
% data is now a cell array with 2 elements, each a (row) cell array
% - the first is all the function names
% - the second is all the hashes
% Transform `data` into {function1, hash1, function2, hash2, ...}'
% First step is to transpose the data concatenate both fields under
% each other. Since MATLAB indexing uses "column major order",
% traversing the concatenated array is in the order we want.
data = [data{:}]';
allValues = data(:)';
else
allValues = {};
end
hashes = struct(allValues{:});
end
end
% ==============================================================================
function bool = isValidAction(str)
% returns true for valid actions (keep/remove/ask) on "removedTests":
bool = ismember(lower(str), {'keep','remove','ask'});
end
function bool = isActualAction(str)
% returns true for actual actions (keep/remove) on "removedTests"
bool = ismember(lower(str), {'keep','remove'});
end
% ==============================================================================

View File

@@ -0,0 +1,104 @@
alphaImage : c1f655e08814b6737d14fc62401464d2
alphaTest : 636ce0a35bfc181f47970031f00f6b72
annotationAll : b600c6654bf983288dd25a24d17b277b
annotationSubplots : c6c07fabe6ef6fc5fb4d0a00f3a98bd2
annotationText : 0e479d484171cbe71208f132319a9dfb
annotationTextUnits : 212301f6fab8fc44936a1d6c8dd3ac59
areaPlot : 53cc00dd9f6059d734722dc7d521be39
axesColors : 863530544e1bd5d2ac2a40d115b0d89c
axesLocation : e7dac4ed9f58c31612496b3dd191b2fe
bars : b6c9b6bd0884fd9041c50ce57486a730
besselImage : 2478d41afe2dbb5e3c7c3ca35433df4b
bodeplots : e58a307dacc26ad38259cff98c141352
clownImage : 50cf1ed3d5954ea574ee1dcf850d2291
colorbarLabelTitle : bb5e997230a6b1330c749d737b73f03b
colorbarLogplot : 4cb17bffe972526eac63da33a9222f6d
colorbarManualLocationLeftIn : 34c149317117365e93c2d4bc87342a36
colorbarManualLocationLeftOut : 61df87739df57ce09e1ff941cede9b11
colorbarManualLocationRightIn : 43929d243b1ce12aecfda3d130edf359
colorbarManualLocationRightOut : ec8ed1290c5a3f45d4cb3795a66c0f1f
colorbars : 271fdd43648c30b49861e5ac492bfe37
compassplot : 746fe6816c8f5e91bc30b748937ddf5f
contourPenny : bd9fe77201617c2aeec42680cf3db0cb
croppedImage : a7df6401799a09cb2868babe95ce4c77
customLegend : cd7e76a0e4394ae7e166b0a38c81be2b
decayingharmonic : 5f430cbacf89268a100b9f0bc6b92874
double_axes : b8d4a8ac79d6de3ff26a8e7b4e1f4cb2
double_axes2 : 481f3f1c2e52f55a82193a39c6f8de28
double_colorbar : 95740167b4b9fe7e082f5e6ff075b0f1
errorBars : e156bb17b2c4938e683d1415cfd539b4
errorBars2 : ca4bd1a1f0e835cb6628c92082534205
fill3plot : 801300e07b52eb484e67abe761b6a0a4
freqResponsePlot : d18c2174642550ca7373be8e207a43e7
herrorbarPlot : f383b91e61b3fbc586b5c02ce2bafbfc
hgTransformPlot : e32beea57daf9940ffd74ae8b1a83403
hist3d : 867f35c157a81cf207d63e558cf167e5
imageOrientation_PNG : c90e5e7bd1575ab8f504a8370a43a21e
imageOrientation_inline : 9f8624bfd743ea0c3c19557e3b8319c5
imagescplot : d1a7958ac53afd332e6fa934eee963f5
imagescplot2 : fad5b247a605964440b150202d4c5a7a
latexInterpreter : 061001fe023e6e404d4fe024ce28d245
latexmath2 : 082e5416e213056d6ebe4a1bccd4286d
legendplot : b005a57b62d43c3808abc57693090ddc
legendplotBoxoff : a6552f6438efa5ae5c1e1b2bd3742d09
legendsubplots : e656f45706bd476cf2401824d1014f8c
linesWithOutliers : 0aa11947614995837eb1ce45265b45f5
logbaseline : 23e4ebf3d9457aa26f940aca27fc32dd
logicalImage : 843c396ce40a2c255d915b87b9b6bc53
logplot : 15455204620fc850b94d721f1f79cacc
mandrillImage : c3d5f63087be1e587af0f3b1f5efa573
manualAlignment : a66a4d684ec5af5b3b967fbd9f9eba40
many_random_points : 44f79b07c32fe2d334cb4d41bb245a2a
markerSizes : c80e1e82fcd9d7cbcb52265cd966273b
markerSizes2 : 2bc71206cca5fecdd89ee0ca6e7dec9a
meshPlot : 390a65be331e6aeab8f4164b36b85e30
mixedBarLine : 1562c32dfa41afadd5e890cc844ce139
multiline_labels : 31fbbf1dcc63516bafa8479c14812706
multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e
multiplePatches : d20f5bcb061e68e4e7848f2115d8589b
myBoxplot : 1811640f9161ebbed0cec285ba423f7d
overlappingPlots : 7e9618c8abc6eef40000202f12e201f8
pColorPlot : edba1f3b39503095cace3d7053ad22a6
parameterCurve3d : 79b6bfee183a996894c7964a023e312f
parameterSurf : f65ce01c7e7a560205ee69e01d318b14
peaks_contour : f6f964a8759939d5948e8fad796d481a
peaks_contourf : 9e960b9d128dfbd102a8bbe2c5ab15ef
pixelLegend : 3ac112865f3b1a50c62fb0b5f19d4215
plain_cos : 9a8455b8cc710436572bc825de0cec78
plotyyLegends : 9ed8515333235d8f84692a7d874e1d57
polarplot : 35bcc693ce44029933edc6039ee4e652
quiver3plot : 49ff594d23510463b75103fd099acf62
quiveroverlap : 2e63a73822cc65d8086d90e23d52cbcf
quiverplot : 7dbf0db4142bdbf7d421e83a66c149a5
randomWithLines : 5227e4b38ad2503e9bae3ec6e566ac4a
rectanglePlot : 0d9b3ef5dd01905fa987d81d139ec5fa
removeOutsideMarker : c5c22d6ad18cb05dff6e0ef9bf0f20bf
rlocusPlot : b2f0237a3bc42ca1f9305dc411437949
roseplot : 3c61d3d9cd107a135ac958e3a2aa5d6c
scatter3Plot : a3c58f7801a9cd7805a0bf888321d570
scatterPlot : c7f71e9961a43df1047e938ff00f45cd
scatterPlotMarkers : 4035e2da2a8e1ddeb4badeede264858c
scatterPlotRandom : 60feab8df90afa2b60d79f98e98b58bc
sine_with_annotation : a770a5ab9552941d0a663d7850785807
sine_with_markers : 003c40a83b2dfaf7b6dfbcb9d74a505d
spectro : 330dde4cc92550a49ea3df07455e6d9c
spherePlot : 93eeb7042de0a8a18a59208d0e50df23
stackedBarsWithOther : 708cb63f69815f47a31b9918c9c2c715
stairsplot : 95ff3a04e9d5fec297d4b52dc895779d
stemplot : e1d5c43f90a3c34b0c0463c10dcd8edd
stemplot2 : 8cbf11bd5d2f388b4b572844f6960e60
subplot2x2b : 71c072af504b2bb208c8d7743ae0e5a2
subplotCustom : 606bc283da5b9c0319c4105910393050
superkohle : 84ab937a68f0969598247b7b19b18cc3
surfPlot : 652c4e1f83e0165199f952b8f1c412bb
surfPlot2 : 026acc60aab65d0567052cbf870a9704
texInterpreter : b67b59f407a551196fa1448d9c35e9e7
texcolor : 7a8b8c01525fc11ac4f1186c803748a9
texrandom : 4cc05e583bb3cfbc9884ed5f03f10e5a
textAlignment : 6d51ee2120803e85237a57a25b6b43e7
textext : 5be451a1b3314c54ee8ba519f6d2cae9
xAxisReversed : 610fc5e22fa2c7efbdf788115cc7eb2d
ylabels : 6daba91575bb4a6c2ff58d8d9a171526
zoom : 4b140788bbd7c2e0f1a4d8b46640c9eb
zplanePlot1 : 44a119eb5b2266b5e765bac21e6a488e
zplanePlot2 : 95db146feb3d517f9e2540a551a50659

View File

@@ -0,0 +1,104 @@
alphaImage : 14978e97c02baf045fb05ed6b4a5b391
alphaTest : d194e2297b5dd5a0bb1282f9142ddda7
annotationAll : 0a8a1578d17a16b3a5db57434069c6fc
annotationSubplots : 30c06f8d5e3d30bbf1ccf8df29576f16
annotationText : 9f293eb4bb31ac29c44d1c772b05ac74
annotationTextUnits : 68f570a8fa1d3a0cec44ef2a1d58aa3b
areaPlot : 3c36bd59014ca6346d6cbbbb6f78943a
axesColors : 863530544e1bd5d2ac2a40d115b0d89c
axesLocation : e7dac4ed9f58c31612496b3dd191b2fe
bars : aa8ef10df99c7d750c92c7ba95a73814
besselImage : c5a0696550fda36e7d2eba7d91643716
bodeplots : a2d8208c81d7f449b51107907c43e053
clownImage : 50cf1ed3d5954ea574ee1dcf850d2291
colorbarLabelTitle : 3ff06d0ee178a052429194d184ee0182
colorbarManualLocationLeftIn : fce39178f29dcdab800d6c99edc70bed
colorbarManualLocationLeftOut : 0b35797bac696ec978b1dcac2c5baa20
colorbarManualLocationRightIn : 776fbc67733cbd2104384d76de954eda
colorbarManualLocationRightOut : fd1a74e61c95b0c185ac26d1cffee4a3
colorbars : 0795ee171bf5954ff7e36d3ddc3867d5
compassplot : bfdceefee37557845bbc0b4c7609ff50
contourPenny : 11b3f1178d9585112b4d40f47a334de0
croppedImage : c3a4a5e6ac11450d797c259a867dfd72
customLegend : cc43f95e60cd8ff447c61fdebbaef18f
decayingharmonic : 2d9be9791b36efc6e1d052769dba3369
double_axes : af07a7caf0effe5eb70fd23f21137ebb
double_axes2 : 6cd1b9ea3b7ae29801738674446e5f64
double_colorbar : 47eaadfd2b100239e63c3175382c0598
errorBars : 9a959889b00bdc80a18aae4cef6fff24
errorBars2 : ee08727841633566b12ee1553f587b98
fill3plot : 801300e07b52eb484e67abe761b6a0a4
freqResponsePlot : f307eac45a335d76278beb4209b7f335
herrorbarPlot : 9f7e3880d3d77af312f5af20ab45d427
hgTransformPlot : 8c0a136c4bb0ec5ff02a344bc9ef9bf0
hist3d : 867f35c157a81cf207d63e558cf167e5
histogramPlot : a96b7898c7c426edc4d8ba680dd4b4c2
imageOrientation_PNG : 94b2656bb5235d842d65085094f0dfc7
imageOrientation_inline : 8f6b04de03cdc80a3ba1b5a35832e8a7
imagescplot : d1a7958ac53afd332e6fa934eee963f5
imagescplot2 : fad5b247a605964440b150202d4c5a7a
latexInterpreter : 827313b6ff9dceb1ca27e814e5d7cc81
latexmath2 : 5359827e1d218311bf80228f89deffdf
legendplot : a61471a19448f4d097c3053de5d4ca13
legendplotBoxoff : a6552f6438efa5ae5c1e1b2bd3742d09
legendsubplots : 474a38fa9ddcf06bfab08a53ddc42032
linesWithOutliers : 0aa11947614995837eb1ce45265b45f5
logbaseline : 86f6627cae4fd5ca8891de0fff521ff4
logicalImage : 843c396ce40a2c255d915b87b9b6bc53
logplot : 5d5c676b2a3338558939d0407171edc6
mandrillImage : c3d5f63087be1e587af0f3b1f5efa573
manualAlignment : a66a4d684ec5af5b3b967fbd9f9eba40
many_random_points : 44f79b07c32fe2d334cb4d41bb245a2a
markerSizes : c80e1e82fcd9d7cbcb52265cd966273b
markerSizes2 : 2bc71206cca5fecdd89ee0ca6e7dec9a
meshPlot : 390a65be331e6aeab8f4164b36b85e30
mixedBarLine : dfcba35ebc32c940566368ea83323add
multiline_labels : d81ea3e1c07d9010027a40854f4e0e4d
multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e
multiplePatches : e8a187bc7c133435cfad346eb7957a9d
myBoxplot : abdb5d198d82e79940999cf61563099f
overlappingPlots : 27f112e843ef7c8835cee164c70f2b1e
pColorPlot : 0dbd6a9f0cc5dd2691d7d3767b831f68
parameterCurve3d : 6aead7a8377449e056856f5b7d6fb9ed
parameterSurf : 0bc50b54c2bff6e22ddf7e9a2a479f7c
peaks_contour : 5e6873909d68b0e397c14eb2deb58e4f
peaks_contourf : 8532510c96c15f96b9a8ad3843446c20
pixelLegend : 3ac112865f3b1a50c62fb0b5f19d4215
plain_cos : 3b0a06de2ae24b7242eef8840acfaca2
plotyyLegends : 9ed8515333235d8f84692a7d874e1d57
polarplot : e232124afee0fdf260dea1cdbb497275
quiver3plot : 5dd5d89ff4c5d3de0c000a999814d10a
quiveroverlap : f977afb99d073b7ea4a54bd7cc254300
quiverplot : 95e36d3db46c861ccfcca981af846aa6
randomWithLines : 9857814e260c4a929f5f0e9e92ba896d
rectanglePlot : 0d9b3ef5dd01905fa987d81d139ec5fa
removeOutsideMarker : f9330795ff85ba1ce4753a700825ab8c
rlocusPlot : 03d2da6924f15785a9f803e0cbe88252
roseplot : b095b86b171a56d817834d803e9e4b6a
scatter3Plot : ed343b46e0c71f8b87b71bd32e645a17
scatterPlot : 5d0b1fca0e65b2f8b16bfa5872f3ba1a
scatterPlotMarkers : 4035e2da2a8e1ddeb4badeede264858c
scatterPlotRandom : 60feab8df90afa2b60d79f98e98b58bc
sine_with_annotation : 98cd4228e6a58210dc10768bc3d48298
sine_with_markers : 003c40a83b2dfaf7b6dfbcb9d74a505d
spectro : 954d57a5d3e0a6b71ed4b530295829c7
spherePlot : a0728a993418536015482b6e2262f530
stackedBarsWithOther : 24f65175516e4af409e0b5a344e69590
stairsplot : 681cd9b7ec346c36ae53de4294148153
stemplot : b20a8531bc0a24b7b9ed161e150c15ff
stemplot2 : ce57617afde9d61311536fb018fc2ace
subplot2x2b : 55c61c78e5b58bea889f6dc4c53cad8e
subplotCustom : ebba99c098189a9e90a69925b4f842c2
superkohle : 6f3903621fbfc0e52c6e5ef379e6cadc
surfPlot : 4b3c88fd47161c784ce117e08e0a4c8c
surfPlot2 : ea785ea64ce8763913edff0ff1846ed1
texInterpreter : b67b59f407a551196fa1448d9c35e9e7
texcolor : 7a8b8c01525fc11ac4f1186c803748a9
texrandom : 111d9b06c1440d1ecf80df9498fb4daf
textAlignment : 8b0a62113daf8ec03a711d1dc7f47e2b
textext : 5be451a1b3314c54ee8ba519f6d2cae9
xAxisReversed : 610fc5e22fa2c7efbdf788115cc7eb2d
ylabels : 0f03d59d01b31956b87861e5705c28f2
zoom : 731634a7fd3efbe0bfc72405614ae3fd
zplanePlot1 : 4d974fa9a55b6298d0637654fd7d86ce
zplanePlot2 : 6b7848acad9ce16f9d3ce303ff85cc17

View File

@@ -0,0 +1,87 @@
alphaImage : a51ff2c6514fb5606ee2fcb732edc7ea
alphaTest : bd2d78b8d373342d5285132b607a2f46
annotationAll : 719ee5b81452f375afa95b757226f6ec
annotationSubplots : aa261262dff0a1f26487d4a0f06cd828
annotationText : c7185fe122b2e3fa5335fbc51bb89103
annotationTextUnits : 1d29851134c715342efc36a690d5b396
areaPlot : e04438af8498d2aa6fba5d242a8290ed
axesColors : 45751a2a4fd30e0c888237721c0ae018
axesLocation : 5189186c3185e8b3fa52622af950a0fd
bars : 15a269e905602896394eabd367040ce1
besselImage : 0ffc3e10adb87029b44cbeb4bc435e07
colorbarLabelTitle : 272a4d2f645b296e43c01f4ab2ae25a4
colorbarLogplot : cb326bbb54e486a40d387acfe82f1b72
colorbarManualLocationLeftIn : d2d90ddaa56b9f13d009a595c21e9601
colorbarManualLocationLeftOut : dea8de2bdf0d866dc3f4f2b8327ea494
colorbarManualLocationRightIn : c8866d76fee1a0876cf3f46aa5bb5761
colorbarManualLocationRightOut : 81b67065a6281c3de6c1a9181d4dfcbc
colorbars : 13b6b31da1f9aef218ef68a6a2c4bbc5
compassplot : 94efa27f5503d3a56e6b40a0ca0f75d1
contourPenny : 3dab076bb0f089b4c30f321a8b732331
customLegend : 7b0d1d80a2f72bc920d869151ffe96c1
decayingharmonic : faaac45ba8f9262cf8a879323d3bf253
double_axes : 243d26b1446d316955cded4098ef76df
double_axes2 : 1fb2d96e2845da4935e251d2e860135c
errorBars : 50b3272a0eff1a540911265dc86840e7
errorBars2 : af50a7241d97ac9e2c78a4f157e056a2
herrorbarPlot : aca7b04d007f2da41617eb027c15b0ef
imageOrientation_PNG : 8086dd5610c028cf75501954cbaccf2c
imageOrientation_inline : 02c0fb3e810aab39cb690e0fa3f8b7eb
imagescplot : 88328b4f32c5ed1cc4e0ad09ea7f47cb
imagescplot2 : 39ce8c49f28f3ce90a6bd5a9d0f1cd42
latexInterpreter : e02f1a2468683a3893b0ba5622ba556d
latexmath2 : 0453c91a38ab2e002b7744825c1aa0e9
legendplot : 5f351094a8e4cd2192fe7cbb2859fd5c
legendplotBoxoff : 9b2f5fb5630b53c4f7e0cbade5c8a719
legendsubplots : 17c08345bafc58e1562984b37d4e01c9
linesWithOutliers : 21dd1eb9763a12d1367a43f2e4314057
logbaseline : 8d62e25d03732cdfc8cb2dd419e0b1b5
logicalImage : 82e9e5ed998aa1ecd78b514d801dc914
logplot : ecb205ea014ba6d0042579c3d608d978
manualAlignment : 91b81ccba3a733f1660de692f1a24ead
many_random_points : 465f2cfaf13757239404db65e9c49da8
markerSizes : f0853e39d2cc6c67ca23306fdb32b6c0
markerSizes2 : ee9ceeb6e9d413b80c070f267914f479
meshPlot : 0ed5e09884b72b670f4d67b134f714f2
mixedBarLine : 124b6efc0a9b4d350e0f179bdcc794c9
multiline_labels : 113098f446ba5b37be05b2e495753a38
multipleAxes : 33ea385933a0e54967fdfd2a763116d6
multiplePatches : 5136db15994352b537d5261586efb68e
overlappingPlots : 6372bbcaaf0f9831a9a371c154c68510
pColorPlot : ea8de8ae9a060827a15589640a7c2af2
parameterCurve3d : d74674982f8de1a07e2d91690442c640
peaks_contour : a08b52735b11941aef4194d42c50ca95
peaks_contourf : bae99cfb0c6d76fbb9eda7cf3253c861
pixelLegend : 88bcddd20a3c5626ef158e2252685ec9
plain_cos : 83d0268e8b3115003942bd1bdaf2672f
plotyyLegends : 4195fadd2c55b022391f8addbbfc2dd5
polarplot : a663c1b56d3073601536d4dacae5e8a0
quiver3plot : 2262caf036feb9b926af20dccee4674d
quiveroverlap : b565fbfd750ff33aa3e24500218458c4
quiverplot : 5183781255b220415251eb7bcb5d8707
randomWithLines : 152796f6be5be2a94832bfb5ea126483
rectanglePlot : 0a6f4e29891fdd8492d821df65ed727f
removeOutsideMarker : c26260549046c7a885617d03c1a6e714
roseplot : 526bbf61758a3ac158b2f1de6a39de73
scatter3Plot : 85a3d342f218f8ec4b0734bbefa13d0c
scatterPlotMarkers : 5dada75b14206174059f357028f82de5
scatterPlotRandom : a8bc3cbcd74de1e38783b974586d875d
sine_with_annotation : 0b0f9823c6c36cae49d5053d8b984884
sine_with_markers : d8761700c3dbfb3a496dc962c9d3ec64
spherePlot : 42ab300428e3248d03105b83cac2b312
stackedBarsWithOther : ac3c350f5af365630e0aaf4352108dd8
stairsplot : 9338e729f5569578228a7bac2d123a9a
stemplot : 557cad38e474e0cd7878b4680c33d291
stemplot2 : 7450fcbc9332ecbd8ac9e801aad4d8fc
subplot2x2b : 7db858e541e4edc35e9721226d8a0f5a
subplotCustom : 588c7323ed9a6a69cbf71849a84d961f
surfPlot : 5f6735d7f3ae960526c10335d3b232b6
surfPlot2 : 30a4abfa8829ce6562f6bceb9e702b74
texInterpreter : 64e4b7edcb6f21f14f3b60544a1f48bc
texcolor : 8b5363038aa4454372cf03ae13f3a91a
texrandom : f85439ecf43d7dc6f150a9977f1900ff
textAlignment : 5e75059b1f661be10d3d790983f20f40
textext : 730e7111a60f2f5fa0a9aa1a4445e242
xAxisReversed : b7c279800afb84deb37cb9208a657a24
ylabels : b7180ba11dd0428064340ab39935e9b1
zoom : 7777afa78e6ee6169841ccd5a894c942

View File

@@ -0,0 +1,87 @@
alphaImage : a51ff2c6514fb5606ee2fcb732edc7ea
alphaTest : 0aabea57e8351373e2c3090fa1929435
annotationAll : 5139104b005a70d3650c6dedb8f1dc7e
annotationSubplots : 3da2ff3cadd2167ab1726c88f18a8a63
annotationText : efe23b1567345ec24564b598c0d2c146
annotationTextUnits : 0c874e12ee2167ffe041b48ab2124417
areaPlot : cdb88d8449ea9275c7eeed21c236e055
axesColors : 45751a2a4fd30e0c888237721c0ae018
axesLocation : 27dd4473cb5871188a20d7e6cbc0d171
bars : 15a269e905602896394eabd367040ce1
besselImage : 0ffc3e10adb87029b44cbeb4bc435e07
colorbarLabelTitle : 272a4d2f645b296e43c01f4ab2ae25a4
colorbarLogplot : cb326bbb54e486a40d387acfe82f1b72
colorbarManualLocationLeftIn : d2d90ddaa56b9f13d009a595c21e9601
colorbarManualLocationLeftOut : dea8de2bdf0d866dc3f4f2b8327ea494
colorbarManualLocationRightIn : c8866d76fee1a0876cf3f46aa5bb5761
colorbarManualLocationRightOut : 81b67065a6281c3de6c1a9181d4dfcbc
colorbars : 13b6b31da1f9aef218ef68a6a2c4bbc5
compassplot : 94efa27f5503d3a56e6b40a0ca0f75d1
contourPenny : fbd639280f4c31dcce7d463e4eac27f4
customLegend : 7b0d1d80a2f72bc920d869151ffe96c1
decayingharmonic : a5551e12b79edd8f32bd5d91ae5fa561
double_axes : 125855bab62dbd8a599bfb6d9d2a041f
double_axes2 : 6cd1b9ea3b7ae29801738674446e5f64
errorBars : 5d31dee2340e12e5b48aad2d93f89b3f
errorBars2 : 85a015a7afc2c6a7046053c887cce43a
herrorbarPlot : f2eaee609ad23ad02d03e78912a5f23a
imageOrientation_PNG : 8086dd5610c028cf75501954cbaccf2c
imageOrientation_inline : 02c0fb3e810aab39cb690e0fa3f8b7eb
imagescplot : 88328b4f32c5ed1cc4e0ad09ea7f47cb
imagescplot2 : 39ce8c49f28f3ce90a6bd5a9d0f1cd42
latexInterpreter : 73b51aed59a6644c15f1561e51ecbf6d
latexmath2 : 5359827e1d218311bf80228f89deffdf
legendplot : 5f351094a8e4cd2192fe7cbb2859fd5c
legendplotBoxoff : 2654de1ee49ac8f4d2546eb6f254952f
legendsubplots : 17c08345bafc58e1562984b37d4e01c9
linesWithOutliers : 0aa11947614995837eb1ce45265b45f5
logbaseline : f23182b1e230b1b21244ad9364475863
logicalImage : 82e9e5ed998aa1ecd78b514d801dc914
logplot : 5d5c676b2a3338558939d0407171edc6
manualAlignment : ff6a5307120276310c01fb6f9c223d77
many_random_points : 46b09dca92fc4f2517a198427f44b874
markerSizes : e0d2628d90419f38457b7ee71d178bb1
markerSizes2 : 560cc9e62fe361aa5456ef87fb715649
meshPlot : 390a65be331e6aeab8f4164b36b85e30
mixedBarLine : 124b6efc0a9b4d350e0f179bdcc794c9
multiline_labels : 113098f446ba5b37be05b2e495753a38
multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e
multiplePatches : 4d77a30b4a6b14772505e0e9c2a727cf
overlappingPlots : 714f8029c2ad4fc86a8fb5323f35b0e4
pColorPlot : ce623ad5a9ebf4b3c74d5954027f0631
parameterCurve3d : ce2a304c64eb5283a40779f6f410d6ba
peaks_contour : a08b52735b11941aef4194d42c50ca95
peaks_contourf : bfe49f12ff8710df9fbf6bd9672ca124
pixelLegend : 2afe53d2be6f01a41b74c5bd892fda83
plain_cos : defebe18ad95f9f1b1f823ad96bd12bd
plotyyLegends : 96cb3c09f470251db4ddf24f59e8e9f4
polarplot : a663c1b56d3073601536d4dacae5e8a0
quiver3plot : 5dd5d89ff4c5d3de0c000a999814d10a
quiveroverlap : f446d413254f3f1e8e4ec449b8a7f813
quiverplot : 22d70477697462b313c3d56ef78ac9c8
randomWithLines : e96d8488671951033bc98d94163a06cb
rectanglePlot : 045df69771942ea322873279a61c7b9f
removeOutsideMarker : f0606cd40ecafc645b10b706d84aa874
roseplot : 526bbf61758a3ac158b2f1de6a39de73
scatter3Plot : c9cc13e983b56272dcbb3c90edd1f8c1
scatterPlotMarkers : a5ec68437ee9e103ec849bdd1ab6657c
scatterPlotRandom : 3dc985fcb9773a82a156321e12e6460b
sine_with_annotation : 0b0f9823c6c36cae49d5053d8b984884
sine_with_markers : 2cf4b72e72482e4b192012c4165ef5b5
spherePlot : 5b0e48733101f43996cc70c2962b52d8
stackedBarsWithOther : ac3c350f5af365630e0aaf4352108dd8
stairsplot : 73051cf170b77661c4ea464718af1ded
stemplot : fcf6f58d74e51684f5aedd090506b562
stemplot2 : 7450fcbc9332ecbd8ac9e801aad4d8fc
subplot2x2b : 7db858e541e4edc35e9721226d8a0f5a
subplotCustom : 14bbb2edbd657c7386139bfd7085f8c9
surfPlot : 2aadedca0c9e2fe866252a5969fdd08a
surfPlot2 : 30a4abfa8829ce6562f6bceb9e702b74
texInterpreter : b67b59f407a551196fa1448d9c35e9e7
texcolor : 7a8b8c01525fc11ac4f1186c803748a9
texrandom : 4dacf9590338ff73553c4a82a78e09bf
textAlignment : 5e75059b1f661be10d3d790983f20f40
textext : 5be451a1b3314c54ee8ba519f6d2cae9
xAxisReversed : 1448dd92ba4ca7c492f060e3d585d340
ylabels : 9e68a9002553bcfe576a22d60ec7c5e9
zoom : 7777afa78e6ee6169841ccd5a894c942

View File

@@ -0,0 +1,87 @@
alphaImage : a51ff2c6514fb5606ee2fcb732edc7ea
alphaTest : 0aabea57e8351373e2c3090fa1929435
annotationAll : 5139104b005a70d3650c6dedb8f1dc7e
annotationSubplots : 3da2ff3cadd2167ab1726c88f18a8a63
annotationText : efe23b1567345ec24564b598c0d2c146
annotationTextUnits : 0c874e12ee2167ffe041b48ab2124417
areaPlot : cdb88d8449ea9275c7eeed21c236e055
axesColors : 45751a2a4fd30e0c888237721c0ae018
axesLocation : 27dd4473cb5871188a20d7e6cbc0d171
bars : 15a269e905602896394eabd367040ce1
besselImage : 0ffc3e10adb87029b44cbeb4bc435e07
colorbarLabelTitle : 272a4d2f645b296e43c01f4ab2ae25a4
colorbarLogplot : cb326bbb54e486a40d387acfe82f1b72
colorbarManualLocationLeftIn : d2d90ddaa56b9f13d009a595c21e9601
colorbarManualLocationLeftOut : dea8de2bdf0d866dc3f4f2b8327ea494
colorbarManualLocationRightIn : c8866d76fee1a0876cf3f46aa5bb5761
colorbarManualLocationRightOut : 81b67065a6281c3de6c1a9181d4dfcbc
colorbars : 13b6b31da1f9aef218ef68a6a2c4bbc5
compassplot : 94efa27f5503d3a56e6b40a0ca0f75d1
contourPenny : fbd639280f4c31dcce7d463e4eac27f4
customLegend : 7b0d1d80a2f72bc920d869151ffe96c1
decayingharmonic : a5551e12b79edd8f32bd5d91ae5fa561
double_axes : 125855bab62dbd8a599bfb6d9d2a041f
double_axes2 : 6cd1b9ea3b7ae29801738674446e5f64
errorBars : 52e1abac8c4ba10d9427cebc9e6845db
errorBars2 : c0750114ad1821a37a833e378217ef5b
herrorbarPlot : 92c6a3ebd7e6194362b31117b1e52d92
imageOrientation_PNG : 8086dd5610c028cf75501954cbaccf2c
imageOrientation_inline : 02c0fb3e810aab39cb690e0fa3f8b7eb
imagescplot : 88328b4f32c5ed1cc4e0ad09ea7f47cb
imagescplot2 : 39ce8c49f28f3ce90a6bd5a9d0f1cd42
latexInterpreter : 73b51aed59a6644c15f1561e51ecbf6d
latexmath2 : 5359827e1d218311bf80228f89deffdf
legendplot : 5f351094a8e4cd2192fe7cbb2859fd5c
legendplotBoxoff : 2654de1ee49ac8f4d2546eb6f254952f
legendsubplots : 17c08345bafc58e1562984b37d4e01c9
linesWithOutliers : 0aa11947614995837eb1ce45265b45f5
logbaseline : f23182b1e230b1b21244ad9364475863
logicalImage : 82e9e5ed998aa1ecd78b514d801dc914
logplot : 5d5c676b2a3338558939d0407171edc6
manualAlignment : ff6a5307120276310c01fb6f9c223d77
many_random_points : 44f79b07c32fe2d334cb4d41bb245a2a
markerSizes : c80e1e82fcd9d7cbcb52265cd966273b
markerSizes2 : 2bc71206cca5fecdd89ee0ca6e7dec9a
meshPlot : 390a65be331e6aeab8f4164b36b85e30
mixedBarLine : 124b6efc0a9b4d350e0f179bdcc794c9
multiline_labels : 113098f446ba5b37be05b2e495753a38
multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e
multiplePatches : 4d77a30b4a6b14772505e0e9c2a727cf
overlappingPlots : 714f8029c2ad4fc86a8fb5323f35b0e4
pColorPlot : ce623ad5a9ebf4b3c74d5954027f0631
parameterCurve3d : ce2a304c64eb5283a40779f6f410d6ba
peaks_contour : a08b52735b11941aef4194d42c50ca95
peaks_contourf : bae99cfb0c6d76fbb9eda7cf3253c861
pixelLegend : 2afe53d2be6f01a41b74c5bd892fda83
plain_cos : defebe18ad95f9f1b1f823ad96bd12bd
plotyyLegends : 96cb3c09f470251db4ddf24f59e8e9f4
polarplot : a663c1b56d3073601536d4dacae5e8a0
quiver3plot : 5dd5d89ff4c5d3de0c000a999814d10a
quiveroverlap : f446d413254f3f1e8e4ec449b8a7f813
quiverplot : 22d70477697462b313c3d56ef78ac9c8
randomWithLines : 9857814e260c4a929f5f0e9e92ba896d
rectanglePlot : 045df69771942ea322873279a61c7b9f
removeOutsideMarker : f9330795ff85ba1ce4753a700825ab8c
roseplot : 526bbf61758a3ac158b2f1de6a39de73
scatter3Plot : cede4e9f5248013f47b95e589d805ea8
scatterPlotMarkers : b9f6262dba1b2035eb14abda46194ba7
scatterPlotRandom : 4b47c90b5493c5e53c3e60a4681e93fd
sine_with_annotation : 0b0f9823c6c36cae49d5053d8b984884
sine_with_markers : 2cf4b72e72482e4b192012c4165ef5b5
spherePlot : 5b0e48733101f43996cc70c2962b52d8
stackedBarsWithOther : ac3c350f5af365630e0aaf4352108dd8
stairsplot : 73051cf170b77661c4ea464718af1ded
stemplot : 983fded97cd2f6cb3286bbcbc5002473
stemplot2 : 7450fcbc9332ecbd8ac9e801aad4d8fc
subplot2x2b : 7db858e541e4edc35e9721226d8a0f5a
subplotCustom : 14bbb2edbd657c7386139bfd7085f8c9
surfPlot : 2aadedca0c9e2fe866252a5969fdd08a
surfPlot2 : 30a4abfa8829ce6562f6bceb9e702b74
texInterpreter : b67b59f407a551196fa1448d9c35e9e7
texcolor : 7a8b8c01525fc11ac4f1186c803748a9
texrandom : 4dacf9590338ff73553c4a82a78e09bf
textAlignment : 5e75059b1f661be10d3d790983f20f40
textext : 5be451a1b3314c54ee8ba519f6d2cae9
xAxisReversed : 1448dd92ba4ca7c492f060e3d585d340
ylabels : 9e68a9002553bcfe576a22d60ec7c5e9
zoom : 7777afa78e6ee6169841ccd5a894c942

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
function [ status ] = issues( k )
%ISSUES M2T Test cases related to issues
%
% Issue-related test cases for matlab2tikz
%
% See also: ACID, matlab2tikz_acidtest
testfunction_handles = {
@scatter3Plot3
};
numFunctions = length( testfunction_handles );
if (k<=0)
status = testfunction_handles;
return; % This is used for querying numFunctions.
elseif (k<=numFunctions)
status = testfunction_handles{k}();
status.function = func2str(testfunction_handles{k});
else
error('issues:outOfBounds', ...
'Out of bounds (number of testfunctions=%d)', numFunctions);
end
end
% =========================================================================
function [stat] = scatter3Plot3()
stat.description = 'Scatter3 plot with 2 colors';
stat.issues = 292;
hold on;
x = sin(1:5);
y = cos(3.4 *(1:5));
z = x.*y;
scatter3(x,y,z,150,...
'MarkerEdgeColor','none','MarkerFaceColor','k');
scatter3(-x,y,z,150,...
'MarkerEdgeColor','none','MarkerFaceColor','b');
end
% =========================================================================

View File

@@ -0,0 +1,25 @@
function [env, versionString] = getEnvironment()
% Determine environment (Octave, MATLAB) and version string
% TODO: Unify private `getEnvironment` functions
persistent cache
if isempty(cache)
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
if isOctave
env = 'Octave';
versionString = OCTAVE_VERSION;
else
env = 'MATLAB';
vData = ver(env);
versionString = vData.Version;
end
% store in cache
cache.env = env;
cache.versionString = versionString;
else
env = cache.env;
versionString = cache.versionString;
end
end

View File

@@ -0,0 +1,167 @@
function hh = herrorbar(x, y, l, u, symbol)
%HERRORBAR Horizontal Error bar plot.
% HERRORBAR(X,Y,L,R) plots the graph of vector X vs. vector Y with
% horizontal error bars specified by the vectors L and R. L and R contain the
% left and right error ranges for each point in X. Each error bar
% is L(i) + R(i) long and is drawn a distance of L(i) to the right and R(i)
% to the right the points in (X,Y). The vectors X,Y,L and R must all be
% the same length. If X,Y,L and R are matrices then each column
% produces a separate line.
%
% HERRORBAR(X,Y,E) or HERRORBAR(Y,E) plots X with error bars [X-E X+E].
% HERRORBAR(...,'LineSpec') uses the color and linestyle specified by
% the string 'LineSpec'. See PLOT for possibilities.
%
% H = HERRORBAR(...) returns a vector of line handles.
%
% Example:
% x = 1:10;
% y = sin(x);
% e = std(y)*ones(size(x));
% herrorbar(x,y,e)
% draws symmetric horizontal error bars of unit standard deviation.
%
% This code is based on ERRORBAR provided in MATLAB.
%
% See also ERRORBAR
% Jos van der Geest
% email: jos@jasen.nl
%
% File history:
% August 2006 (Jos): I have taken back ownership. I like to thank Greg Aloe from
% The MathWorks who originally introduced this piece of code to the
% Matlab File Exchange.
% September 2003 (Greg Aloe): This code was originally provided by Jos
% from the newsgroup comp.soft-sys.matlab:
% http://newsreader.mathworks.com/WebX?50@118.fdnxaJz9btF^1@.eea3ff9
% After unsuccessfully attempting to contact the orignal author, I
% decided to take ownership so that others could benefit from finding it
% on the MATLAB Central File Exchange.
if min(size(x))==1,
npt = length(x);
x = x(:);
y = y(:);
if nargin > 2,
if ~ischar(l),
l = l(:);
end
if nargin > 3
if ~ischar(u)
u = u(:);
end
end
end
else
[npt,n] = size(x);
end
if nargin == 3
if ~ischar(l)
u = l;
symbol = '-';
else
symbol = l;
l = y;
u = y;
y = x;
[m,n] = size(y);
x(:) = (1:npt)'*ones(1,n);;
end
end
if nargin == 4
if ischar(u),
symbol = u;
u = l;
else
symbol = '-';
end
end
if nargin == 2
l = y;
u = y;
y = x;
[m,n] = size(y);
x(:) = (1:npt)'*ones(1,n);;
symbol = '-';
end
u = abs(u);
l = abs(l);
if ischar(x) || ischar(y) || ischar(u) || ischar(l)
error('Arguments must be numeric.')
end
if ~isequal(size(x),size(y)) || ~isequal(size(x),size(l)) || ~isequal(size(x),size(u)),
error('The sizes of X, Y, L and U must be the same.');
end
tee = (max(y(:))-min(y(:)))/100; % make tee .02 x-distance for error bars
% changed from errorbar.m
xl = x - l;
xr = x + u;
ytop = y + tee;
ybot = y - tee;
n = size(y,2);
% end change
% Plot graph and bars
hold_state = ishold;
cax = newplot;
next = lower(get(cax,'NextPlot'));
% build up nan-separated vector for bars
% changed from errorbar.m
xb = zeros(npt*9,n);
xb(1:9:end,:) = xl;
xb(2:9:end,:) = xl;
xb(3:9:end,:) = NaN;
xb(4:9:end,:) = xl;
xb(5:9:end,:) = xr;
xb(6:9:end,:) = NaN;
xb(7:9:end,:) = xr;
xb(8:9:end,:) = xr;
xb(9:9:end,:) = NaN;
yb = zeros(npt*9,n);
yb(1:9:end,:) = ytop;
yb(2:9:end,:) = ybot;
yb(3:9:end,:) = NaN;
yb(4:9:end,:) = y;
yb(5:9:end,:) = y;
yb(6:9:end,:) = NaN;
yb(7:9:end,:) = ytop;
yb(8:9:end,:) = ybot;
yb(9:9:end,:) = NaN;
% end change
[ls,col,mark,msg] = colstyle(symbol);
if ~isempty(msg)
error(msg);
end
if isempty(col)
col = '';
end
symbol = [ls mark col]; % Use marker only on data part
esymbol = ['-' col]; % Make sure bars are solid
if ~isempty(strfind(symbol,'none'))
symbol = 'none';
end
if ~isempty(strfind(esymbol,'none'))
esymbol = 'none';
end
h = plot(xb,yb,'LineStyle',esymbol); hold on
h = [h;plot(x,y,'LineStyle',symbol)];
if ~hold_state
hold off;
end
if nargout>0
hh = h;
end

View File

@@ -0,0 +1,46 @@
function bool = isEnvironment(wantedEnvironment, varargin)
% ISENVIRONMENT check for a particular environment (MATLAB/Octave)
%
% This function returns TRUE when it is run within the "wantedEnvironment"
% (e.g. MATLAB or Octave). This environment can be tested to be a particular
% version or be older/newer than a specified version.
%
% Usage:
%
% ISENVIRONMENT(ENV)
% ISENVIRONMENT(ENV, VERSION)
% ISENVIRONMENT(ENV, OP, VERSION)
%
% Parameters:
% - `ENV`: the expected environment (e.g. 'MATLAB' or 'Octave')
% - `VERSION`: a version number or string to compare against
% e.g. "3.4" or equivalently [3,4]
% - `OP`: comparison operator (e.g. '==', '<=', '<', ...) to define a range
% of version numbers that return a TRUE value
%
% When `OP` is not specified, "==" is used.
% When no `VERSION` is specified, all versions pass the check.
%
% See also: isMATLAB, isOctave, versionCompare
[env, thisVersion] = getEnvironment();
bool = strcmpi(env, wantedEnvironment);
switch numel(varargin)
case 0 % nothing to be done
return
case 1 % check equality
version = varargin{1};
operator = '==';
bool = bool && versionCompare(thisVersion, operator, version);
case 2
operator = varargin{1};
version = varargin{2};
bool = bool && versionCompare(thisVersion, operator, version);
otherwise
error('isEnvironment:BadNumberOfArguments', ...
'"isEnvironment" was called with an incorrect number of arguments.');
end
end

View File

@@ -0,0 +1,4 @@
function bool = isMATLAB(varargin)
%ISMATLAB Determines whether (a certain) version of MATLAB is being used
% See also: isEnvironment, isOctave
bool = isEnvironment('MATLAB', varargin{:});

View File

@@ -0,0 +1,5 @@
function bool = isOctave(varargin)
%ISOCTAVE Determines whether (a certain) version of Octave is being used
%
% See also: isEnvironment, isMATLAB
bool = isEnvironment('Octave', varargin{:});

View File

@@ -0,0 +1,38 @@
function isBelow = isVersionBelow(versionA, versionB)
% Checks if versionA is smaller than versionB
vA = versionArray(versionA);
vB = versionArray(versionB);
n = min(length(vA), length(vB));
deltaAB = vA(1:n) - vB(1:n);
difference = find(deltaAB, 1, 'first');
if isempty(difference)
isBelow = false; % equal versions
else
isBelow = (deltaAB(difference) < 0);
end
end
% ==============================================================================
function arr = versionArray(str)
% Converts a version string to an array.
if ischar(str)
% Translate version string from '2.62.8.1' to [2; 62; 8; 1].
switch getEnvironment
case 'MATLAB'
split = regexp(str, '\.', 'split'); % compatibility MATLAB < R2013a
case 'Octave'
split = strsplit(str, '.');
otherwise
errorUnknownEnvironment();
end
arr = str2num(char(split)); %#ok
else
arr = str;
end
arr = arr(:)';
end
% ==============================================================================
function errorUnknownEnvironment()
error('matlab2tikz:unknownEnvironment',...
'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment);
end
% ==============================================================================

View File

@@ -0,0 +1,20 @@
function bool = versionCompare( vA, operator, vB )
%VERSIONCOMPARE Performs a version comparison operation
switch operator
case '<'
bool = isVersionBelow(vA, vB);
case '>'
bool = isVersionBelow(vB, vA);
case {'<=', '=<'}
bool = ~isVersionBelow(vB, vA);
case {'>=', '=>'}
bool = ~isVersionBelow(vA, vB);
case {'=', '=='}
bool = ~isVersionBelow(vA, vB) && ~isVersionBelow(vB, vA);
case {'~=', '!='}
bool = isVersionBelow(vA, vB) || isVersionBelow(vB, vA);
otherwise
error('versionCompare:UnknownOperator',...
'"%s" is not a known comparison operator', operator);
end
end

View File

@@ -0,0 +1,121 @@
function status = testPatches(k)
% TESTPATCHES Test suite for patches
%
% See also: ACID, matlab2tikz_acidtest
testfunction_handles = {
@patch01;
@patch02;
@patch03;
@patch04;
@patch05;
@patch06;
@patch07;
@patch08;
};
numFunctions = length( testfunction_handles );
if nargin < 1 || isempty(k) || k <= 0
status = testfunction_handles;
return; % This is used for querying numFunctions.
elseif (k<=numFunctions)
status = testfunction_handles{k}();
status.function = func2str(testfunction_handles{k});
else
error('patchTests:outOfBounds', ...
'Out of bounds (number of testfunctions=%d)', numFunctions);
end
end
% =========================================================================
function p = patch00()
% DO NOT INCLUDE IN ACID LIST
% Base patch plot for following tests
xdata = [2 2 0 2 5; 2 8 2 4 5; 8 8 2 4 8];
ydata = [4 4 4 2 0; 8 4 6 2 2; 4 0 4 0 0];
zdata = ones(3,5)*2;
p = patch(xdata,ydata,zdata);
end
% =========================================================================
function stat = patch01()
stat.description = 'Set face color red';
p = patch00();
set(p,'FaceColor','r')
end
% =========================================================================
function stat = patch02()
stat.description = 'Flat face colors scaled in clim [0,40]';
p = patch00();
set(gca,'CLim',[0 40])
cdata = [15 30 25 2 60];
set(p,'FaceColor','flat','CData',cdata,'CDataMapping','scaled')
end
% =========================================================================
function stat = patch03()
stat.description = 'Flat face colors direct in clim [0,40]';
p = patch00();
set(gca,'CLim',[0 40])
cdata = [15 30 25 2 60];
set(p,'FaceColor','flat','CData',cdata,'CDataMapping','direct')
end
% =========================================================================
function stat = patch04()
stat.description = 'Flat face colors with 3D (truecolor) CData';
p = patch00();
cdata(:,:,1) = [0 0 1 0 0.8];
cdata(:,:,2) = [0 0 0 0 0.8];
cdata(:,:,3) = [1 1 1 0 0.8];
set(p,'FaceColor','flat','CData',cdata)
end
% =========================================================================
function stat = patch05()
stat.description = 'Flat face color, scaled edge colors in clim [0,40]';
p = patch00();
set(gca,'CLim',[0 40])
cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42];
set(p,'FaceColor','flat','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','scaled')
end
% =========================================================================
function stat = patch06()
stat.description = 'Flat face color, direct edge colors in clim [0,40]';
p = patch00();
set(gca,'CLim',[0 40])
cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42];
set(p,'FaceColor','flat','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','direct')
end
% =========================================================================
function stat = patch07()
stat.description = 'Flat face color with 3D CData and interp edge colors';
p = patch00();
cdata(:,:,1) = [0 0 1 0 0.8;
0 0 1 0.2 0.6;
0 1 0 0.4 1];
cdata(:,:,2) = [0 0 0 0 0.8;
1 1 1 0.2 0.6;
1 0 0 0.4 0];
cdata(:,:,3) = [1 1 1 0 0.8;
0 1 0 0.2 0.6;
1 0 1 0.4 0];
set(p,'FaceColor','flat','CData',cdata,'EdgeColor','interp','LineWidth',5)
end
% =========================================================================
function stat = patch08()
stat.description = 'Interp face colors, flat edges, scaled CData in clims [0,40]';
p = patch00();
set(gca,'CLim',[0 40])
cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42];
set(p,'FaceColor','interp','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','scaled')
end
% =========================================================================

View File

@@ -0,0 +1,102 @@
function status = testSurfshader(k)
% TESTSURFSHADER Test suite for Surf/mesh shaders (coloring)
%
% See also: ACID, matlab2tikz_acidtest
testfunction_handles = {
@surfShader1;
@surfShader2;
@surfShader3;
@surfShader4;
@surfShader5;
@surfNoShader;
@surfNoPlot;
@surfMeshInterp;
@surfMeshRGB;
};
numFunctions = length( testfunction_handles );
if nargin < 1 || isempty(k) || k <= 0
status = testfunction_handles;
return; % This is used for querying numFunctions.
elseif (k<=numFunctions)
status = testfunction_handles{k}();
status.function = func2str(testfunction_handles{k});
else
error('patchTests:outOfBounds', ...
'Out of bounds (number of testfunctions=%d)', numFunctions);
end
end
% =========================================================================
function [stat] = surfShader1()
stat.description = 'shader=flat/(flat mean) | Fc: flat | Ec: none';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','flat','EdgeColor','none')
end
% =========================================================================
function [stat] = surfShader2()
stat.description = 'shader=interp | Fc: interp | Ec: none';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','interp','EdgeColor','none')
end
% =========================================================================
function [stat] = surfShader3()
stat.description = 'shader=faceted | Fc: flat | Ec: RGB';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','flat','EdgeColor','green')
end
% =========================================================================
function [stat] = surfShader4()
stat.description = 'shader=faceted | Fc: RGB | Ec: interp';
if isMATLAB('<', [8,4]); %R2014a and older
warning('m2t:ACID:surfShader4',...
'The MATLAB EPS export may behave strangely for this case');
end
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','blue','EdgeColor','interp')
end
% =========================================================================
function [stat] = surfShader5()
stat.description = 'shader=faceted interp | Fc: interp | Ec: flat';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','interp','EdgeColor','flat')
end
% =========================================================================
function [stat] = surfNoShader()
stat.description = 'no shader | Fc: RGB | Ec: RGB';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','blue','EdgeColor','yellow')
end
% =========================================================================
function [stat] = surfNoPlot()
stat.description = 'no plot | Fc: none | Ec: none';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','none','EdgeColor','none')
end
% =========================================================================
function [stat] = surfMeshInterp()
stat.description = 'mesh | Fc: none | Ec: interp';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','none','EdgeColor','interp')
end
% =========================================================================
function [stat] = surfMeshRGB()
stat.description = 'mesh | Fc: none | Ec: RGB';
[X,Y,Z] = peaks(5);
surf(X,Y,Z,'FaceColor','none','EdgeColor','green')
end
% =========================================================================

View File

@@ -0,0 +1,6 @@
# just ignore all generate files
acid.*
*.log
*.aux
*.pdf
*.tex

View File

@@ -0,0 +1,33 @@
# ./Makefile
ECHOCMD:=/bin/echo -e
LATEX:=pdflatex --shell-escape
TARGET:=acid
main:
cd data/reference/ && $(MAKE)
cd data/converted/ && $(MAKE)
@$(LATEX) $(TARGET)
.PHONY: clean
clean:
@rm -f $(TARGET).aux \
$(TARGET).log \
$(TARGET).nav \
$(TARGET).out \
$(TARGET).snm \
$(TARGET).toc \
$(TARGET).vrb \
$(TARGET).pdf \
$(TARGET).dvi \
$(TARGET).ps \
missfont.log
@rm -f *~
cd data/reference/ && $(MAKE) clean
cd data/converted/ && $(MAKE) clean
distclean: clean
@rm -f $(TARGET).tex
cd data/reference/ && $(MAKE) distclean
cd data/converted/ && $(MAKE) distclean

View File

@@ -0,0 +1,8 @@
# just ignore all generated files
*.log
*.aux
*.pdf
*.eps
*.png
*.tex
*.tsv

View File

@@ -0,0 +1,23 @@
# ./Makefile
ECHOCMD:=/bin/echo -e
LATEX:=pdflatex --shell-escape -interaction=batchmode
TEST_SRCS:=$(wildcard test*-converted.tex)
TEST_PDFS:=$(TEST_SRCS:.tex=.pdf)
default: $(TEST_PDFS)
%.pdf: %.tex
@$(LATEX) $<
.PHONY: clean
clean:
rm -f test*-converted.aux \
test*-converted.log \
test*-converted.pdf
distclean: clean
rm -f test*-converted.tex \
test*-converted*.png

View File

@@ -0,0 +1,19 @@
# ./Makefile
EPSTOPDF:=epstopdf
REFERENCE_EPSS:=$(wildcard test*-reference.eps)
REFERENCE_PDFS:=$(REFERENCE_EPSS:.eps=.pdf)
default: $(REFERENCE_PDFS)
%.pdf: %.eps
$(EPSTOPDF) $<
.PHONY: clean
clean:
rm -f test*-reference.pdf
distclean: clean
rm -f test*-reference.eps

View File

@@ -0,0 +1,47 @@
function [ status ] = testGraphical( varargin )
%TESTGRAPHICAL Runs the M2T test suite to produce graphical output
%
% This is quite a thin wrapper around testMatlab2tikz to run the test suite to
% produce a PDF side-by-side report.
%
% Its allowed arguments are the same as those of testMatlab2tikz.
%
% Usage:
%
% status = testGraphical(...) % gives programmatical access to the data
%
% testGraphical(...); % automatically invokes makeLatexReport afterwards
%
% See also: testMatlab2tikz, testHeadless, makeLatexReport
[state] = initializeGlobalState();
finally_restore_state = onCleanup(@() restoreGlobalState(state));
[status, args] = testMatlab2tikz('actionsToExecute', @actionsToExecute, ...
varargin{:});
if nargout == 0
makeLatexReport(status, args.output);
end
end
% ==============================================================================
function status = actionsToExecute(status, ipp)
status = execute_plot_stage(status, ipp);
if status.skip
return
end
status = execute_save_stage(status, ipp);
status = execute_tikz_stage(status, ipp);
%status = execute_hash_stage(status, ipp); %cannot work with files in
%standalone mode!
status = execute_type_stage(status, ipp);
if ~status.closeall && ~isempty(status.plotStage.fig_handle)
close(status.plotStage.fig_handle);
else
close all;
end
end
% ==============================================================================

View File

@@ -0,0 +1,62 @@
function [ status ] = testHeadless( varargin )
%TESTGRAPHICAL Runs the M2T test suite without graphical output
%
% This is quite a thin wrapper around testMatlab2tikz to run the test suite to
% produce a textual report and checks for regressions by checking the MD5 hash
% of the output
%
% Its allowed arguments are the same as those of testMatlab2tikz.
%
% Usage:
%
% status = TESTHEADLESS(...) % gives programmatical access to the data
%
% TESTHEADLESS(...); % automatically invokes makeTravisReport afterwards
%
% See also: testMatlab2tikz, testGraphical, makeTravisReport
% The width and height are specified to circumvent different DPIs in developer
% machines. The float format reduces the probability that numerical differences
% in the order of numerical precision disrupt the output.
extraOptions = {'width' ,'\figureWidth', ...
'height','\figureHeight',...
'floatFormat', '%4g', ... % see #604
'extraCode',{ ...
'\newlength\figureHeight \setlength{\figureHeight}{6cm}', ...
'\newlength\figureWidth \setlength{\figureWidth}{10cm}'}
};
[state] = initializeGlobalState();
finally_restore_state = onCleanup(@() restoreGlobalState(state));
status = testMatlab2tikz('extraOptions', extraOptions, ...
'actionsToExecute', @actionsToExecute, ...
varargin{:});
if nargout == 0
makeTravisReport(status);
end
end
% ==============================================================================
function status = actionsToExecute(status, ipp)
status = execute_plot_stage(status, ipp);
if status.skip
return
end
status = execute_tikz_stage(status, ipp);
status = execute_hash_stage(status, ipp);
status = execute_type_stage(status, ipp);
if ~status.closeall && ~isempty(status.plotStage.fig_handle)
try
close(status.plotStage.fig_handle);
catch
close('all');
end
else
close all;
end
end
% ==============================================================================