5589
|
1 ## Copyright (C) 2000 Paul Kienzle |
|
2 ## |
|
3 ## This program is free software; you can redistribute it and/or modify |
|
4 ## it under the terms of the GNU General Public License as published by |
|
5 ## the Free Software Foundation; either version 2 of the License, or |
|
6 ## (at your option) any later version. |
|
7 ## |
|
8 ## This program is distributed in the hope that it will be useful, |
|
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 ## GNU General Public License for more details. |
|
12 ## |
|
13 ## You should have received a copy of the GNU General Public License |
|
14 ## along with this program; if not, write to the Free Software |
|
15 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
16 ## 02110-1301 USA |
|
17 |
|
18 ## -*- texinfo -*- |
|
19 ## @deftypefn {Function File} {} assert (@var{cond}) |
|
20 ## @deftypefnx {Function File} {} assert (@var{observed},@var{expected}) |
|
21 ## @deftypefnx {Function File} {} assert (@var{observed},@var{expected},@var{tol}) |
|
22 ## |
|
23 ## Produces an error if the condition is not met. @code{assert} can be |
|
24 ## called in three different ways. |
|
25 ## |
|
26 ## @table @code |
|
27 ## @item assert (@var{cond}) |
|
28 ## Called with a single argument @var{cond}, @code{assert} produces an |
|
29 ## error if @var{cond} is zero. |
|
30 ## |
|
31 ## @item assert (@var{observed}, @var{expected}) |
|
32 ## Produce an error if observed is not the same as expected. Note that |
|
33 ## observed and expected can be strings, scalars, vectors, matrices, |
|
34 ## lists or structures. |
|
35 ## |
|
36 ## @item assert(@var{observed}, @var{expected}, @var{tol}) |
|
37 ## Produce an error if relative error is less than tolerance. That is, |
|
38 ## @code{abs(@var{observed} - @var{expected}) > @var{tol} * @var{expected}}. |
|
39 ## Absolute error @code{abs(@var{observed} - @var{expected}) > abs(@var{tol})} |
|
40 ## will be used when tolerance is negative or when the expected value is zero. |
|
41 ## @end table |
5642
|
42 ## @seealso{test} |
5589
|
43 ## @end deftypefn |
|
44 |
|
45 ## TODO: Output throttling: don't print out the entire 100x100 matrix, |
|
46 ## TODO: but instead give a summary; don't print out the whole list, just |
|
47 ## TODO: say what the first different element is, etc. To do this, make |
|
48 ## TODO: the message generation type specific. |
6494
|
49 |
|
50 function assert (cond, expected, tol) |
5589
|
51 |
|
52 if (nargin < 1 || nargin > 3) |
6046
|
53 print_usage (); |
5589
|
54 endif |
|
55 |
|
56 if (nargin < 3) |
|
57 tol = 0; |
|
58 endif |
|
59 |
6494
|
60 if (exist ("argn") == 0) |
|
61 argn = " "; |
|
62 endif |
|
63 |
|
64 in = deblank (argn(1,:)); |
|
65 for i = 2:rows (argn) |
|
66 in = strcat (in, ",", deblank (argn(i,:))); |
5589
|
67 end |
6494
|
68 in = strcat ("(", in, ")"); |
5589
|
69 |
|
70 coda = ""; |
|
71 iserror = 0; |
|
72 if (nargin == 1) |
6494
|
73 if (! isnumeric (cond) || ! all (cond(:))) |
5589
|
74 error ("assert %s failed", in); # say which elements failed? |
|
75 endif |
|
76 |
6494
|
77 elseif (is_list (cond)) |
|
78 if (! is_list (expected) || length (cond) != length (expected)) |
5589
|
79 iserror = 1; |
|
80 else |
|
81 try |
6494
|
82 for i = 1:length (cond) |
|
83 assert (nth (cond, i), nth (expected, i)); |
5589
|
84 endfor |
|
85 catch |
|
86 iserror = 1; |
|
87 end |
|
88 endif |
|
89 |
|
90 elseif (ischar (expected)) |
6494
|
91 iserror = (! ischar (cond) || ! strcmp (cond, expected)); |
5589
|
92 |
6494
|
93 elseif (iscell (expected)) |
|
94 if (! iscell (cond) || any (size (cond) != size (expected))) |
5589
|
95 iserror = 1; |
|
96 else |
|
97 try |
6494
|
98 for i = 1:length (expected(:)) |
|
99 assert (cond{i}, expected{i}, tol); |
5589
|
100 endfor |
|
101 catch |
|
102 iserror = 1; |
|
103 end |
|
104 endif |
|
105 |
|
106 elseif (isstruct (expected)) |
6494
|
107 if (! isstruct (cond) || any (size (cond) != size (expected)) |
|
108 || rows(struct_elements (cond)) != rows (struct_elements (expected))) |
5589
|
109 iserror = 1; |
|
110 else |
|
111 try |
6494
|
112 empty = numel (cond) == 0; |
|
113 normal = numel (cond) == 1; |
|
114 for [v, k] = cond |
|
115 if (! struct_contains (expected, k)) |
|
116 error (); |
|
117 endif |
|
118 if (empty) |
|
119 v = cell (1, 0); |
|
120 endif |
|
121 if (normal) |
|
122 v = {v}; |
|
123 else |
|
124 v = v(:)'; |
|
125 endif |
|
126 assert (v, {expected.(k)}, tol); |
5589
|
127 endfor |
|
128 catch |
|
129 iserror = 1; |
|
130 end |
|
131 endif |
|
132 |
6392
|
133 elseif (ndims (cond) != ndims (expected) |
|
134 || any (size (cond) != size (expected))) |
5589
|
135 iserror = 1; |
|
136 coda = "Dimensions don't match"; |
|
137 |
6494
|
138 elseif (tol == 0 && ! strcmp (typeinfo (cond), typeinfo (expected))) |
5589
|
139 iserror = 1; |
6494
|
140 coda = strcat ("Type ", typeinfo (cond), " != ", typeinfo (expected)); |
5589
|
141 |
|
142 else # numeric |
6494
|
143 A = cond(:); |
|
144 B = expected(:); |
5589
|
145 ## Check exceptional values |
6494
|
146 if (any (isna (A) != isna (B))) |
|
147 iserror = 1; |
|
148 coda = "NAs don't match"; |
|
149 elseif (any (isnan (A) != isnan (B))) |
5589
|
150 iserror = 1; |
|
151 coda = "NaNs don't match"; |
6494
|
152 ### Try to avoid problems comparing strange values like Inf+NaNi. |
|
153 elseif (any (isinf (A) != isinf (B)) |
|
154 || any (A(isinf (A) & ! isnan (A)) != B(isinf (B) & ! isnan (B)))) |
5589
|
155 iserror = 1; |
|
156 coda = "Infs don't match"; |
|
157 else |
|
158 ## Check normal values |
6494
|
159 A = A(finite (A)); |
|
160 B = B(finite (B)); |
|
161 if (tol == 0) |
|
162 err = any (A != B); |
5589
|
163 errtype = "values do not match"; |
6494
|
164 elseif (tol >= 0) |
|
165 err = max (abs (A - B)); |
5589
|
166 errtype = "maximum absolute error %g exceeds tolerance %g"; |
|
167 else |
6494
|
168 abserr = max (abs (A(B == 0))); |
|
169 A = A(B != 0); |
|
170 B = B(B != 0); |
|
171 relerr = max (abs (A - B) ./ abs (B)); |
|
172 err = max ([abserr; relerr]); |
5589
|
173 errtype = "maximum relative error %g exceeds tolerance %g"; |
|
174 endif |
6494
|
175 if (err > abs (tol)) |
5589
|
176 iserror = 1; |
6494
|
177 coda = sprintf (errtype, err, abs (tol)); |
5589
|
178 endif |
|
179 endif |
|
180 endif |
|
181 |
6494
|
182 if (! iserror) |
5589
|
183 return; |
|
184 endif |
|
185 |
|
186 ## pretty print the "expected but got" info, |
|
187 ## trimming leading and trailing "\n" |
|
188 str = disp (expected); |
6494
|
189 idx = find (str != "\n"); |
|
190 if (! isempty (idx)) |
|
191 str = str(idx(1):idx(end)); |
5589
|
192 endif |
|
193 str2 = disp (cond); |
6494
|
194 idx = find (str2 != "\n"); |
|
195 if (! isempty (idx)) |
|
196 str2 = str2 (idx(1):idx(end)); |
5589
|
197 endif |
6494
|
198 msg = strcat ("assert ", in, " expected\n", str, "\nbut got\n", str2); |
|
199 if (! isempty (coda)) |
|
200 msg = strcat (msg, "\n", coda); |
5589
|
201 endif |
6494
|
202 error ("%s", msg); |
|
203 ## disp (msg); |
|
204 ## error ("assertion failed"); |
5589
|
205 endfunction |
|
206 |
|
207 ## empty |
|
208 %!assert([]) |
|
209 %!assert(zeros(3,0),zeros(3,0)) |
|
210 %!error assert(zeros(3,0),zeros(0,2)) |
|
211 %!error assert(zeros(3,0),[]) |
6455
|
212 %!fail("assert(zeros(2,0,2),zeros(2,0))", "Dimensions don't match") |
5589
|
213 |
|
214 ## conditions |
|
215 %!assert(isempty([])) |
|
216 %!assert(1) |
|
217 %!error assert(0) |
|
218 %!assert(ones(3,1)) |
|
219 %!assert(ones(1,3)) |
|
220 %!assert(ones(3,4)) |
|
221 %!error assert([1,0,1]) |
|
222 %!error assert([1;1;0]) |
|
223 %!error assert([1,0;1,1]) |
|
224 |
|
225 ## vectors |
|
226 %!assert([1,2,3],[1,2,3]); |
|
227 %!assert([1;2;3],[1;2;3]); |
|
228 %!error assert([2;2;3],[1;2;3]); |
|
229 %!error assert([1,2,3],[1;2;3]); |
|
230 %!error assert([1,2],[1,2,3]); |
|
231 %!error assert([1;2;3],[1;2]); |
|
232 %!assert([1,2;3,4],[1,2;3,4]); |
|
233 %!error assert([1,4;3,4],[1,2;3,4]) |
|
234 %!error assert([1,3;2,4;3,5],[1,2;3,4]) |
|
235 |
|
236 ## exceptional values |
|
237 %!assert([NaN, NA, Inf, -Inf, 1+eps, eps],[NaN, NA, Inf, -Inf, 1, 0],eps) |
|
238 %!error assert(NaN, 1) |
|
239 %!error assert(NA, 1) |
|
240 %!error assert(-Inf, Inf) |
|
241 |
|
242 ## scalars |
|
243 %!error assert(3, [3,3; 3,3]) |
|
244 %!error assert([3,3; 3,3], 3) |
|
245 %!assert(3, 3); |
|
246 %!assert(3+eps, 3, eps); |
|
247 %!assert(3, 3+eps, eps); |
|
248 %!error assert(3+2*eps, 3, eps); |
|
249 %!error assert(3, 3+2*eps, eps); |
|
250 |
|
251 %## must give a little space for floating point errors on relative |
|
252 %!assert(100+100*eps, 100, -2*eps); |
|
253 %!assert(100, 100+100*eps, -2*eps); |
|
254 %!error assert(100+300*eps, 100, -2*eps); |
|
255 %!error assert(100, 100+300*eps, -2*eps); |
|
256 %!error assert(3, [3,3]); |
|
257 %!error assert(3,4); |
|
258 |
|
259 ## structures |
|
260 %!shared x,y |
|
261 %! x.a = 1; x.b=[2, 2]; |
|
262 %! y.a = 1; y.b=[2, 2]; |
|
263 %!assert (x,y) |
|
264 %!test y.b=3; |
|
265 %!error assert (x,y) |
|
266 %!error assert (3, x); |
|
267 %!error assert (x, 3); |
|
268 |
|
269 ## check usage statements |
|
270 %!error assert |
|
271 %!error assert(1,2,3,4,5) |
|
272 |
|
273 ## strings |
|
274 %!assert("dog","dog") |
|
275 %!error assert("dog","cat") |
|
276 %!error assert("dog",3); |
|
277 %!error assert(3,"dog"); |