]> Creatis software - CreaPhase.git/blob - octave_packages/m/testfun/assert.m
update packages
[CreaPhase.git] / octave_packages / m / testfun / assert.m
1 ## Copyright (C) 2000-2012 Paul Kienzle
2 ##
3 ## This file is part of Octave.
4 ##
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
9 ##
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 ## General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING.  If not, see
17 ## <http://www.gnu.org/licenses/>.
18
19 ## -*- texinfo -*-
20 ## @deftypefn  {Function File} {} assert (@var{cond})
21 ## @deftypefnx {Function File} {} assert (@var{cond}, @var{errmsg}, @dots{})
22 ## @deftypefnx {Function File} {} assert (@var{cond}, @var{msg_id}, @var{errmsg}, @dots{})
23 ## @deftypefnx {Function File} {} assert (@var{observed}, @var{expected})
24 ## @deftypefnx {Function File} {} assert (@var{observed}, @var{expected}, @var{tol})
25 ##
26 ## Produce an error if the specified condition is not met.  @code{assert} can
27 ## be called in three different ways.
28 ##
29 ## @table @code
30 ## @item assert (@var{cond})
31 ## @itemx assert (@var{cond}, @var{errmsg}, @dots{})
32 ## @itemx assert (@var{cond}, @var{msg_id}, @var{errmsg}, @dots{})
33 ## Called with a single argument @var{cond}, @code{assert} produces an
34 ## error if @var{cond} is zero.  When called with more than one argument the
35 ## additional arguments are passed to the @code{error} function.
36 ##
37 ## @item assert (@var{observed}, @var{expected})
38 ## Produce an error if observed is not the same as expected.  Note that
39 ## @var{observed} and @var{expected} can be scalars, vectors, matrices,
40 ## strings, cell arrays, or structures.
41 ##
42 ## @item assert (@var{observed}, @var{expected}, @var{tol})
43 ## Produce an error if observed is not the same as expected but equality
44 ## comparison for numeric data uses a tolerance @var{tol}.
45 ## If @var{tol} is positive then it is an absolute tolerance which will produce
46 ## an error if @code{abs(@var{observed} - @var{expected}) > abs(@var{tol})}.
47 ## If @var{tol} is negative then it is a relative tolerance which will produce
48 ## an error if @code{abs(@var{observed} - @var{expected}) >
49 ## abs(@var{tol} * @var{expected})}.  If @var{expected} is zero @var{tol} will
50 ## always be interpreted as an absolute tolerance.
51 ## @end table
52 ## @seealso{test, fail, error}
53 ## @end deftypefn
54
55 ## FIXME: Output throttling: don't print out the entire 100x100 matrix,
56 ## but instead give a summary; don't print out the whole list, just
57 ## say what the first different element is, etc.  To do this, make
58 ## the message generation type specific.
59
60 function assert (cond, varargin)
61
62   in = deblank (argn(1,:));
63   for i = 2:rows (argn)
64     in = cstrcat (in, ",", deblank (argn(i,:)));
65   endfor
66   in = cstrcat ("(", in, ")");
67
68   if (nargin == 1 || (nargin > 1 && islogical (cond) && ischar (varargin{1})))
69     if ((! isnumeric (cond) && ! islogical (cond)) || ! all (cond(:)))
70       if (nargin == 1)
71         ## Say which elements failed?
72         error ("assert %s failed", in);
73       else
74         error (varargin{:});
75       endif
76     endif
77   else
78     if (nargin < 2 || nargin > 3)
79       print_usage ();
80     endif
81
82     expected = varargin{1};
83     if (nargin < 3)
84       tol = 0;
85     else
86       tol = varargin{2};
87     endif
88
89     if (exist ("argn") == 0)
90       argn = " ";
91     endif
92
93     coda = "";
94     iserror = 0;
95
96
97     if (ischar (expected))
98       iserror = (! ischar (cond) || ! strcmp (cond, expected));
99
100     elseif (iscell (expected))
101       if (! iscell (cond) || any (size (cond) != size (expected)))
102         iserror = 1;
103       else
104         try
105           for i = 1:length (expected(:))
106             assert (cond{i}, expected{i}, tol);
107           endfor
108         catch
109           iserror = 1;
110         end_try_catch
111       endif
112
113     elseif (isstruct (expected))
114       if (! isstruct (cond) || any (size (cond) != size (expected))
115           || rows (fieldnames (cond)) != rows (fieldnames (expected)))
116         iserror = 1;
117       else
118         try
119           #empty = numel (cond) == 0;
120           empty = isempty (cond);
121           normal = (numel (cond) == 1);
122           for [v, k] = cond
123             if (! isfield (expected, k))
124               error ();
125             endif
126             if (empty)
127               v = {};
128             elseif (normal)
129               v = {v};
130             else
131               v = v(:)';
132             endif
133             assert (v, {expected.(k)}, tol);
134           endfor
135         catch
136           iserror = 1;
137         end_try_catch
138       endif
139
140     elseif (ndims (cond) != ndims (expected)
141             || any (size (cond) != size (expected)))
142       iserror = 1;
143       coda = "Dimensions don't match";
144
145     else
146       if (nargin < 3)
147         ## Without explicit tolerance, be more strict.
148         if (! strcmp (class (cond), class (expected)))
149           iserror = 1;
150           coda = cstrcat ("Class ", class (cond), " != ", class (expected));
151         elseif (isnumeric (cond))
152           if (issparse (cond) != issparse (expected))
153             if (issparse (cond))
154               iserror = 1;
155               coda = "sparse != non-sparse";
156             else
157               iserror = 1;
158               coda = "non-sparse != sparse";
159             endif
160           elseif (iscomplex (cond) != iscomplex (expected))
161             if (iscomplex (cond))
162               iserror = 1;
163               coda = "complex != real";
164             else
165               iserror = 1;
166               coda = "real != complex";
167             endif
168           endif
169         endif
170       endif
171
172       if (! iserror)
173         ## Numeric.
174         A = cond(:);
175         B = expected(:);
176         ## Check exceptional values.
177         if (any (isna (A) != isna (B)))
178           iserror = 1;
179           coda = "NAs don't match";
180         elseif (any (isnan (A) != isnan (B)))
181           iserror = 1;
182           coda = "NaNs don't match";
183           ## Try to avoid problems comparing strange values like Inf+NaNi.
184         elseif (any (isinf (A) != isinf (B))
185                 || any (A(isinf (A) & ! isnan (A)) != B(isinf (B) & ! isnan (B))))
186           iserror = 1;
187           coda = "Infs don't match";
188         else
189           ## Check normal values.
190           A = A(isfinite (A));
191           B = B(isfinite (B));
192           if (tol == 0)
193             err = any (A != B);
194             errtype = "values do not match";
195           elseif (tol >= 0)
196             err = max (abs (A - B));
197             errtype = "maximum absolute error %g exceeds tolerance %g";
198           else
199             abserr = max (abs (A(B == 0)));
200             A = A(B != 0);
201             B = B(B != 0);
202             relerr = max (abs (A - B) ./ abs (B));
203             err = max ([abserr; relerr]);
204             errtype = "maximum relative error %g exceeds tolerance %g";
205           endif
206           if (err > abs (tol))
207             iserror = 1;
208             coda = sprintf (errtype, err, abs (tol));
209           endif
210         endif
211       endif
212
213     endif
214
215     if (! iserror)
216       return;
217     endif
218
219     ## Pretty print the "expected but got" info, trimming leading and
220     ## trailing "\n".
221     str = disp (expected);
222     idx = find (str != "\n");
223     if (! isempty (idx))
224       str = str(idx(1):idx(end));
225     endif
226     str2 = disp (cond);
227     idx = find (str2 != "\n");
228     if (! isempty (idx))
229       str2 = str2 (idx(1):idx(end));
230     endif
231     msg = cstrcat ("assert ", in, " expected\n", str, "\nbut got\n", str2);
232     if (! isempty (coda))
233       msg = cstrcat (msg, "\n", coda);
234     endif
235     error ("%s", msg);
236   endif
237
238 endfunction
239
240
241 ## empty input
242 %!assert ([])
243 %!assert (zeros (3,0), zeros (3,0))
244 %!error assert (zeros (3,0), zeros (0,2))
245 %!error assert (zeros (3,0), [])
246 %!error <Dimensions don't match> assert (zeros (2,0,2), zeros (2,0))
247
248 ## conditions
249 %!assert (isempty ([]))
250 %!assert (1)
251 %!error assert (0)
252 %!assert (ones(3,1))
253 %!assert (ones(1,3))
254 %!assert (ones(3,4))
255 %!error assert ([1,0,1])
256 %!error assert ([1;1;0])
257 %!error assert ([1,0;1,1])
258
259 ## scalars
260 %!error assert (3, [3,3; 3,3])
261 %!error assert ([3,3; 3,3], 3)
262 %!assert (3, 3)
263 %!assert (3+eps, 3, eps)
264 %!assert (3, 3+eps, eps)
265 %!error assert (3+2*eps, 3, eps)
266 %!error assert (3, 3+2*eps, eps)
267
268 ## vectors
269 %!assert ([1,2,3],[1,2,3]);
270 %!assert ([1;2;3],[1;2;3]);
271 %!error assert ([2;2;3],[1;2;3]);
272 %!error assert ([1,2,3],[1;2;3]);
273 %!error assert ([1,2],[1,2,3]);
274 %!error assert ([1;2;3],[1;2]);
275 %!assert ([1,2;3,4],[1,2;3,4]);
276 %!error assert ([1,4;3,4],[1,2;3,4])
277 %!error assert ([1,3;2,4;3,5],[1,2;3,4])
278
279 ## must give a small tolerance for floating point errors on relative
280 %!assert (100+100*eps, 100, -2*eps)
281 %!assert (100, 100+100*eps, -2*eps)
282 %!error assert (100+300*eps, 100, -2*eps)
283 %!error assert (100, 100+300*eps, -2*eps)
284 %!error assert (3, [3,3])
285 %!error assert (3, 4)
286
287 ## test relative vs. absolute tolerances
288 %!test  assert (0.1+eps, 0.1,  2*eps);  # accept absolute
289 %!error assert (0.1+eps, 0.1, -2*eps);  # fail relative
290 %!test  assert (100+100*eps, 100, -2*eps);  # accept relative
291 %!error assert (100+100*eps, 100,  2*eps);  # fail absolute
292
293 ## exceptional values
294 %!assert ([NaN, NA, Inf, -Inf, 1+eps, eps], [NaN, NA, Inf, -Inf, 1, 0], eps)
295 %!error assert (NaN, 1)
296 %!error assert (NA, 1)
297 %!error assert (-Inf, Inf)
298
299 ## strings
300 %!assert ("dog", "dog")
301 %!error assert ("dog", "cat")
302 %!error assert ("dog", 3)
303 %!error assert (3, "dog")
304
305 ## structures
306 %!shared x,y
307 %! x.a = 1; x.b=[2, 2];
308 %! y.a = 1; y.b=[2, 2];
309 %!assert (x, y)
310 %!test y.b=3;
311 %!error assert (x, y)
312 %!error assert (3, x)
313 %!error assert (x, 3)
314 %!test
315 %! # Empty structures
316 %! x = resize (x, 0, 1);
317 %! y = resize (y, 0, 1);
318 %! assert (x, y);
319
320 ## cell arrays
321 %!test
322 %! x = {[3], [1,2,3]; 100+100*eps, "dog"};
323 %! y = x;
324 %! assert (x, y);
325 %! y = x; y(1,1) = [2];
326 %! fail ("assert (x, y)");
327 %! y = x; y(1,2) = [0, 2, 3];
328 %! fail ("assert (x, y)");
329 %! y = x; y(2,1) = 101;
330 %! fail ("assert (x, y)");
331 %! y = x; y(2,2) = "cat";
332 %! fail ("assert (x, y)");
333
334 %% Test input validation
335 %!error assert
336 %!error assert (1,2,3,4)
337