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. |
|
49 function assert(cond, expected, tol) |
|
50 |
|
51 if (nargin < 1 || nargin > 3) |
|
52 usage("assert (cond) or assert (v, expected_v [,tol])"); |
|
53 endif |
|
54 |
|
55 if (nargin < 3) |
|
56 tol = 0; |
|
57 endif |
|
58 |
|
59 if exist("argn") == 0, argn=" "; endif |
|
60 in = deblank(argn(1,:)); |
|
61 for i=2:rows(argn) |
|
62 in = [in, ",", deblank(argn(i,:))]; |
|
63 end |
|
64 in = ["(",in,")"]; |
|
65 |
|
66 coda = ""; |
|
67 iserror = 0; |
|
68 if (nargin == 1) |
|
69 if (!isnumeric(cond) || !all(cond(:))) |
|
70 error ("assert %s failed", in); # say which elements failed? |
|
71 endif |
|
72 |
|
73 elseif (is_list(cond)) |
|
74 if (!is_list(expected) || length(cond) != length(expected)) |
|
75 iserror = 1; |
|
76 else |
|
77 try |
|
78 for i=1:length(cond) |
|
79 assert(nth(cond,i),nth(expected,i)); |
|
80 endfor |
|
81 catch |
|
82 iserror = 1; |
|
83 end |
|
84 endif |
|
85 |
|
86 elseif (ischar (expected)) |
|
87 iserror = (!ischar (cond) || !strcmp (cond, expected)); |
|
88 |
|
89 elseif (iscell(expected)) |
|
90 if (!iscell (cond) || any(size(cond)!=size(expected))) |
|
91 iserror = 1; |
|
92 else |
|
93 try |
|
94 for i=1:length(expected(:)) |
|
95 assert(cond{i},expected{i},tol); |
|
96 endfor |
|
97 catch |
|
98 iserror = 1; |
|
99 end |
|
100 endif |
|
101 |
|
102 elseif (isstruct (expected)) |
|
103 if (!isstruct (cond) || any(size(cond) != size(expected)) || ... |
|
104 rows(struct_elements(cond)) != rows(struct_elements(expected))) |
|
105 iserror = 1; |
|
106 else |
|
107 try |
|
108 empty=prod(size(cond))==0; |
|
109 normal=prod(size(cond))==1; |
|
110 for [v,k] = cond |
|
111 if !struct_contains(expected,k), error; endif |
|
112 if empty, v = cell(1,0); endif |
|
113 if normal, v = {v}; endif |
|
114 assert(v,{expected.(k)},tol); |
|
115 endfor |
|
116 catch |
|
117 iserror = 1; |
|
118 end |
|
119 endif |
|
120 |
|
121 elseif (isempty (expected)) |
|
122 iserror = (any (size (cond) != size (expected))); |
|
123 |
|
124 elseif (any (size (cond) != size (expected))) |
|
125 iserror = 1; |
|
126 coda = "Dimensions don't match"; |
|
127 |
|
128 elseif tol==0 && !strcmp(typeinfo(cond),typeinfo(expected)) |
|
129 iserror = 1; |
|
130 coda = ["Type ",typeinfo(cond)," != ",typeinfo(expected)]; |
|
131 |
|
132 else # numeric |
|
133 A=cond(:); B=expected(:); |
|
134 ## Check exceptional values |
|
135 if any(isnan(A) != isnan(B)) |
|
136 iserror = 1; |
|
137 coda = "NaNs don't match"; |
|
138 elseif any(isna(A) != isna(B)) |
|
139 iserror = 1; |
|
140 coda = "NAs don't match"; |
|
141 elseif any(A(isinf(A)) != B(isinf(B))) |
|
142 iserror = 1; |
|
143 coda = "Infs don't match"; |
|
144 else |
|
145 ## Check normal values |
|
146 A = A(finite(A)); B=B(finite(B)); |
|
147 if tol == 0, |
|
148 err = any(A != B); |
|
149 errtype = "values do not match"; |
|
150 elseif tol >= 0, |
|
151 err = max(abs(A-B)); |
|
152 errtype = "maximum absolute error %g exceeds tolerance %g"; |
|
153 else |
|
154 abserr = max(abs(A(B==0))); |
|
155 A = A(B!=0); B = B(B!=0); |
|
156 relerr = max(abs(A-B)./abs(B)); |
|
157 err = max([abserr;relerr]); |
|
158 errtype = "maximum relative error %g exceeds tolerance %g"; |
|
159 endif |
|
160 if err > abs(tol) |
|
161 iserror = 1; |
|
162 coda = sprintf(errtype,err,abs(tol)); |
|
163 endif |
|
164 endif |
|
165 endif |
|
166 |
|
167 if (!iserror) |
|
168 return; |
|
169 endif |
|
170 |
|
171 ## pretty print the "expected but got" info, |
|
172 ## trimming leading and trailing "\n" |
|
173 str = disp (expected); |
|
174 idx = find(str!="\n"); |
|
175 if (!isempty(idx)) |
|
176 str = str(idx(1):idx(length(idx))); |
|
177 endif |
|
178 str2 = disp (cond); |
|
179 idx = find(str2!="\n"); |
|
180 if (!isempty(idx)) |
|
181 str2 = str2(idx(1):idx(length(idx))); |
|
182 endif |
|
183 msg = ["assert ",in," expected\n", str, "\nbut got\n", str2]; |
|
184 if (!isempty(coda)) |
|
185 msg = [ msg, "\n", coda ]; |
|
186 endif |
|
187 error("%s",msg); |
|
188 ## disp(msg); |
|
189 ## error("assertion failed"); |
|
190 endfunction |
|
191 |
|
192 ## empty |
|
193 %!assert([]) |
|
194 %!assert(zeros(3,0),zeros(3,0)) |
|
195 %!error assert(zeros(3,0),zeros(0,2)) |
|
196 %!error assert(zeros(3,0),[]) |
|
197 |
|
198 ## conditions |
|
199 %!assert(isempty([])) |
|
200 %!assert(1) |
|
201 %!error assert(0) |
|
202 %!assert(ones(3,1)) |
|
203 %!assert(ones(1,3)) |
|
204 %!assert(ones(3,4)) |
|
205 %!error assert([1,0,1]) |
|
206 %!error assert([1;1;0]) |
|
207 %!error assert([1,0;1,1]) |
|
208 |
|
209 ## vectors |
|
210 %!assert([1,2,3],[1,2,3]); |
|
211 %!assert([1;2;3],[1;2;3]); |
|
212 %!error assert([2;2;3],[1;2;3]); |
|
213 %!error assert([1,2,3],[1;2;3]); |
|
214 %!error assert([1,2],[1,2,3]); |
|
215 %!error assert([1;2;3],[1;2]); |
|
216 %!assert([1,2;3,4],[1,2;3,4]); |
|
217 %!error assert([1,4;3,4],[1,2;3,4]) |
|
218 %!error assert([1,3;2,4;3,5],[1,2;3,4]) |
|
219 |
|
220 ## exceptional values |
|
221 %!assert([NaN, NA, Inf, -Inf, 1+eps, eps],[NaN, NA, Inf, -Inf, 1, 0],eps) |
|
222 %!error assert(NaN, 1) |
|
223 %!error assert(NA, 1) |
|
224 %!error assert(-Inf, Inf) |
|
225 |
|
226 ## scalars |
|
227 %!error assert(3, [3,3; 3,3]) |
|
228 %!error assert([3,3; 3,3], 3) |
|
229 %!assert(3, 3); |
|
230 %!assert(3+eps, 3, eps); |
|
231 %!assert(3, 3+eps, eps); |
|
232 %!error assert(3+2*eps, 3, eps); |
|
233 %!error assert(3, 3+2*eps, eps); |
|
234 |
|
235 %## must give a little space for floating point errors on relative |
|
236 %!assert(100+100*eps, 100, -2*eps); |
|
237 %!assert(100, 100+100*eps, -2*eps); |
|
238 %!error assert(100+300*eps, 100, -2*eps); |
|
239 %!error assert(100, 100+300*eps, -2*eps); |
|
240 %!error assert(3, [3,3]); |
|
241 %!error assert(3,4); |
|
242 |
|
243 ## structures |
|
244 %!shared x,y |
|
245 %! x.a = 1; x.b=[2, 2]; |
|
246 %! y.a = 1; y.b=[2, 2]; |
|
247 %!assert (x,y) |
|
248 %!test y.b=3; |
|
249 %!error assert (x,y) |
|
250 %!error assert (3, x); |
|
251 %!error assert (x, 3); |
|
252 |
|
253 ## check usage statements |
|
254 %!error assert |
|
255 %!error assert(1,2,3,4,5) |
|
256 |
|
257 ## strings |
|
258 %!assert("dog","dog") |
|
259 %!error assert("dog","cat") |
|
260 %!error assert("dog",3); |
|
261 %!error assert(3,"dog"); |