changelog shortlog tags changeset files revisions annotate raw

scripts/testfun/assert.m

changeset 10289: 4b124317dc38
parent:cb4a4119a21a
author: John W. Eaton <jwe@octave.org>
date: Tue Feb 09 20:58:55 2010 -0500 (35 minutes ago)
permissions: -rw-r--r--
description: base_properties::set_children: account for hidden children
1## Copyright (C) 2000, 2006, 2007, 2008, 2009 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## Produces an error if the condition is not met. @code{assert} can be
27## 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. If called with a single argument a
35## generic error message. With more than one argument, the additional
36## arguments are passed to the @code{error} function.
37##
38## @item assert (@var{observed}, @var{expected})
39## Produce an error if observed is not the same as expected. Note that
40## observed and expected can be strings, scalars, vectors, matrices,
41## lists or structures.
42##
43## @item assert(@var{observed}, @var{expected}, @var{tol})
44## Accept a tolerance when comparing numbers.
45## If @var{tol} is positive use it as an absolute tolerance, will produce an error if
46## @code{abs(@var{observed} - @var{expected}) > abs(@var{tol})}.
47## If @var{tol} is negative use it as a relative tolerance, will produce an error if
48## @code{abs(@var{observed} - @var{expected}) > abs(@var{tol} * @var{expected})}.
49## If @var{expected} is zero @var{tol} will always be used as an absolute tolerance.
50## @end table
51## @seealso{test}
52## @end deftypefn
53
54## FIXME: Output throttling: don't print out the entire 100x100 matrix,
55## but instead give a summary; don't print out the whole list, just
56## say what the first different element is, etc. To do this, make
57## the message generation type specific.
58
59function assert (cond, varargin)
60
61 in = deblank (argn(1,:));
62 for i = 2:rows (argn)
63 in = cstrcat (in, ",", deblank (argn(i,:)));
64 endfor
65 in = cstrcat ("(", in, ")");
66
67 if (nargin == 1 || (nargin > 1 && islogical (cond) && ischar (varargin{1})))
68 if ((! isnumeric (cond) && ! islogical (cond)) || ! all (cond(:)))
69 if (nargin == 1)
70 ## Say which elements failed?
71 error ("assert %s failed", in);
72 else
73 error (varargin{:});
74 endif
75 endif
76 else
77 if (nargin < 2 || nargin > 3)
78 print_usage ();
79 endif
80
81 expected = varargin {1};
82 if (nargin < 3)
83 tol = 0;
84 else
85 tol = varargin {2};
86 endif
87
88 if (exist ("argn") == 0)
89 argn = " ";
90 endif
91
92 coda = "";
93 iserror = 0;
94
95
96 if (ischar (expected))
97 iserror = (! ischar (cond) || ! strcmp (cond, expected));
98
99 elseif (iscell (expected))
100 if (! iscell (cond) || any (size (cond) != size (expected)))
101 iserror = 1;
102 else
103 try
104 for i = 1:length (expected(:))
105 assert (cond{i}, expected{i}, tol);
106 endfor
107 catch
108 iserror = 1;
109 end_try_catch
110 endif
111
112 elseif (isstruct (expected))
113 if (! isstruct (cond) || any (size (cond) != size (expected))
114 || rows (fieldnames (cond)) != rows (fieldnames (expected)))
115 iserror = 1;
116 else
117 try
118 empty = numel (cond) == 0;
119 normal = numel (cond) == 1;
120 for [v, k] = cond
121 if (! isfield (expected, k))
122 error ();
123 endif
124 if (empty)
125 v = cell (1, 0);
126 endif
127 if (normal)
128 v = {v};
129 else
130 v = v(:)';
131 endif
132 assert (v, {expected.(k)}, tol);
133 endfor
134 catch
135 iserror = 1;
136 end_try_catch
137 endif
138
139 elseif (ndims (cond) != ndims (expected)
140 || any (size (cond) != size (expected)))
141 iserror = 1;
142 coda = "Dimensions don't match";
143
144 else
145 if (nargin < 3)
146 ## Without explicit tolerance, be more strict.
147 if (! strcmp(class (cond), class (expected)))
148 iserror = 1;
149 coda = cstrcat ("Class ", class (cond), " != ", class (expected));
150 elseif (isnumeric (cond))
151 if (issparse (cond) != issparse (expected))
152 if (issparse (cond))
153 iserror = 1;
154 coda = "sparse != non-sparse";
155 else
156 iserror = 1;
157 coda = "non-sparse != sparse";
158 endif
159 elseif (iscomplex (cond) != iscomplex (expected))
160 if (iscomplex (cond))
161 iserror = 1;
162 coda = "complex != real";
163 else
164 iserror = 1;
165 coda = "real != complex";
166 endif
167 endif
168 endif
169 endif
170
171 if (! iserror)
172 ## Numeric.
173 A = cond(:);
174 B = expected(:);
175 ## Check exceptional values.
176 if (any (isna (A) != isna (B)))
177 iserror = 1;
178 coda = "NAs don't match";
179 elseif (any (isnan (A) != isnan (B)))
180 iserror = 1;
181 coda = "NaNs don't match";
182 ## Try to avoid problems comparing strange values like Inf+NaNi.
183 elseif (any (isinf (A) != isinf (B))
184 || any (A(isinf (A) & ! isnan (A)) != B(isinf (B) & ! isnan (B))))
185 iserror = 1;
186 coda = "Infs don't match";
187 else
188 ## Check normal values.
189 A = A(finite (A));
190 B = B(finite (B));
191 if (tol == 0)
192 err = any (A != B);
193 errtype = "values do not match";
194 elseif (tol >= 0)
195 err = max (abs (A - B));
196 errtype = "maximum absolute error %g exceeds tolerance %g";
197 else
198 abserr = max (abs (A(B == 0)));
199 A = A(B != 0);
200 B = B(B != 0);
201 relerr = max (abs (A - B) ./ abs (B));
202 err = max ([abserr; relerr]);
203 errtype = "maximum relative error %g exceeds tolerance %g";
204 endif
205 if (err > abs (tol))
206 iserror = 1;
207 coda = sprintf (errtype, err, abs (tol));
208 endif
209 endif
210 endif
211
212 endif
213
214 if (! iserror)
215 return;
216 endif
217
218 ## Pretty print the "expected but got" info, trimming leading and
219 ## trailing "\n".
220 str = disp (expected);
221 idx = find (str != "\n");
222 if (! isempty (idx))
223 str = str(idx(1):idx(end));
224 endif
225 str2 = disp (cond);
226 idx = find (str2 != "\n");
227 if (! isempty (idx))
228 str2 = str2 (idx(1):idx(end));
229 endif
230 msg = cstrcat ("assert ", in, " expected\n", str, "\nbut got\n", str2);
231 if (! isempty (coda))
232 msg = cstrcat (msg, "\n", coda);
233 endif
234 error ("%s", msg);
235 ## disp (msg);
236 ## error ("assertion failed");
237 endif
238endfunction
239
240## empty
241%!assert([])
242%!assert(zeros(3,0),zeros(3,0))
243%!error assert(zeros(3,0),zeros(0,2))
244%!error assert(zeros(3,0),[])
245%!fail("assert(zeros(2,0,2),zeros(2,0))", "Dimensions don't match")
246
247## conditions
248%!assert(isempty([]))
249%!assert(1)
250%!error assert(0)
251%!assert(ones(3,1))
252%!assert(ones(1,3))
253%!assert(ones(3,4))
254%!error assert([1,0,1])
255%!error assert([1;1;0])
256%!error assert([1,0;1,1])
257
258## vectors
259%!assert([1,2,3],[1,2,3]);
260%!assert([1;2;3],[1;2;3]);
261%!error assert([2;2;3],[1;2;3]);
262%!error assert([1,2,3],[1;2;3]);
263%!error assert([1,2],[1,2,3]);
264%!error assert([1;2;3],[1;2]);
265%!assert([1,2;3,4],[1,2;3,4]);
266%!error assert([1,4;3,4],[1,2;3,4])
267%!error assert([1,3;2,4;3,5],[1,2;3,4])
268
269## exceptional values
270%!assert([NaN, NA, Inf, -Inf, 1+eps, eps],[NaN, NA, Inf, -Inf, 1, 0],eps)
271%!error assert(NaN, 1)
272%!error assert(NA, 1)
273%!error assert(-Inf, Inf)
274
275## scalars
276%!error assert(3, [3,3; 3,3])
277%!error assert([3,3; 3,3], 3)
278%!assert(3, 3);
279%!assert(3+eps, 3, eps);
280%!assert(3, 3+eps, eps);
281%!error assert(3+2*eps, 3, eps);
282%!error assert(3, 3+2*eps, eps);
283
284## must give a little space for floating point errors on relative
285%!assert(100+100*eps, 100, -2*eps);
286%!assert(100, 100+100*eps, -2*eps);
287%!error assert(100+300*eps, 100, -2*eps);
288%!error assert(100, 100+300*eps, -2*eps);
289%!error assert(3, [3,3]);
290%!error assert(3,4);
291
292## test relative vs. absolute tolerances
293%!test assert (0.1+eps, 0.1, 2*eps); # accept absolute
294%!error assert (0.1+eps, 0.1, -2*eps); # fail relative
295%!test assert (100+100*eps, 100, -2*eps); # accept relative
296%!error assert (100+100*eps, 100, 2*eps); # fail absolute
297
298## structures
299%!shared x,y
300%! x.a = 1; x.b=[2, 2];
301%! y.a = 1; y.b=[2, 2];
302%!assert (x,y)
303%!test y.b=3;
304%!error assert (x,y)
305%!error assert (3, x);
306%!error assert (x, 3);
307
308## check usage statements
309%!error assert
310%!error assert(1,2,3,4,5)
311
312## strings
313%!assert("dog","dog")
314%!error assert("dog","cat")
315%!error assert("dog",3);
316%!error assert(3,"dog");