1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(andor_SUITE).
21
22-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24	 t_case/1,t_and_or/1,t_andalso/1,t_orelse/1,inside/1,overlap/1,
25	 combined/1,in_case/1,slow_compilation/1]).
26
27-include_lib("common_test/include/ct.hrl").
28
29suite() -> [{ct_hooks,[ts_install_cth]}].
30
31all() ->
32    [{group,p}].
33
34groups() ->
35    [{p,[parallel],
36      [t_case,t_and_or,t_andalso,t_orelse,inside,overlap,
37       combined,in_case,slow_compilation]}].
38
39init_per_suite(Config) ->
40    test_lib:recompile(?MODULE),
41    Config.
42
43end_per_suite(_Config) ->
44    ok.
45
46init_per_group(_GroupName, Config) ->
47	Config.
48
49end_per_group(_GroupName, Config) ->
50	Config.
51
52
53t_case(Config) when is_list(Config) ->
54    %% We test boolean cases almost but not quite like cases
55    %% generated by andalso/orelse.
56    less = t_case_a(1, 2),
57    not_less = t_case_a(2, 2),
58    {'EXIT',{{case_clause,false},_}} = (catch t_case_b({x,y,z}, 2)),
59    {'EXIT',{{case_clause,true},_}} = (catch t_case_b(a, a)),
60    eq = t_case_c(a, a),
61    ne = t_case_c(42, []),
62    t = t_case_d(x, x, true),
63    f = t_case_d(x, x, false),
64    f = t_case_d(x, y, true),
65    {'EXIT',{badarg,_}} = (catch t_case_d(x, y, blurf)),
66    true = (catch t_case_e({a,b}, {a,b})),
67    false = (catch t_case_e({a,b}, 42)),
68
69    true = t_case_xy(42, 100, 700),
70    true = t_case_xy(42, 100, whatever),
71    false = t_case_xy(42, wrong, 700),
72    false = t_case_xy(42, wrong, whatever),
73
74    true = t_case_xy(0, whatever, 700),
75    true = t_case_xy(0, 100, 700),
76    false = t_case_xy(0, whatever, wrong),
77    false = t_case_xy(0, 100, wrong),
78
79    ok.
80
81t_case_a(A, B) ->
82    case A < B of
83	[_|_] -> ok;
84	true -> less;
85	false -> not_less;
86	{a,b,c} -> ok;
87	_Var -> ok
88    end.
89
90t_case_b(A, B) ->
91    case A =:= B of
92	blurf -> ok
93    end.
94
95t_case_c(A, B) ->
96    case not(A =:= B) of
97	true -> ne;
98	false -> eq
99    end.
100
101t_case_d(A, B, X) ->
102    case (A =:= B) and X of
103	true -> t;
104	false -> f
105    end.
106
107t_case_e(A, B) ->
108    case A =:= B of
109	Bool when is_tuple(A) -> id(Bool)
110    end.
111
112t_case_xy(X, Y, Z) ->
113    Res = t_case_x(X, Y, Z),
114    Res = t_case_y(X, Y, Z).
115
116t_case_x(X, Y, Z) ->
117    case abs(X) =:= 42 of
118	true ->
119	    Y =:= 100;
120	false ->
121	    Z =:= 700
122    end.
123
124t_case_y(X, Y, Z) ->
125    case abs(X) =:= 42 of
126	false ->
127	    Z =:= 700;
128	true ->
129	    Y =:= 100
130    end.
131
132-define(GUARD(E), if E -> true;
133             true -> false
134          end).
135
136t_and_or(Config) when is_list(Config) ->
137    true = true and true,
138    false = true and false,
139    false = false and true,
140    false = false and false,
141
142    true = id(true) and true,
143    false = id(true) and false,
144    false = id(false) and true,
145    false = id(false) and false,
146
147    true = true and id(true),
148    false = true and id(false),
149    false = false and id(true),
150    false = false and id(false),
151
152    true = true or true,
153    true = true or false,
154    true = false or true,
155    false = false or false,
156
157    true = id(true) or true,
158    true = id(true) or false,
159    true = id(false) or true,
160    false = id(false) or false,
161
162    true = true or id(true),
163    true = true or id(false),
164    true = false or id(true),
165    false = false or id(false),
166
167    True = id(true),
168
169    false = ?GUARD(erlang:'and'(bar, True)),
170    false = ?GUARD(erlang:'or'(bar, True)),
171    false = ?GUARD(erlang:'not'(erlang:'and'(bar, True))),
172    false = ?GUARD(erlang:'not'(erlang:'not'(erlang:'and'(bar, True)))),
173
174    true = (fun (X = true) when X or true or X -> true end)(True),
175
176    Tuple = id({a,b}),
177    case Tuple of
178	{_,_} ->
179	    {'EXIT',{badarg,_}} = (catch true and Tuple)
180    end,
181
182    ok.
183
184t_andalso(Config) when is_list(Config) ->
185    Bs = [true,false],
186    Ps = [{X,Y} || X <- Bs, Y <- Bs],
187    lists:foreach(fun (P) -> t_andalso_1(P) end, Ps),
188
189    true = true andalso true,
190    false = true andalso false,
191    false = false andalso true,
192    false = false andalso false,
193
194    true = ?GUARD(true andalso true),
195    false = ?GUARD(true andalso false),
196    false = ?GUARD(false andalso true),
197    false = ?GUARD(false andalso false),
198
199    false = false andalso glurf,
200    false = false andalso exit(exit_now),
201
202    true = not id(false) andalso not id(false),
203    false = not id(false) andalso not id(true),
204    false = not id(true) andalso not id(false),
205    false = not id(true) andalso not id(true),
206
207    {'EXIT',{badarg,_}} = (catch not id(glurf) andalso id(true)),
208    {'EXIT',{badarg,_}} = (catch not id(false) andalso not id(glurf)),
209    false = id(false) andalso not id(glurf),
210    false = false andalso not id(glurf),
211
212    true = begin (X1 = true) andalso X1, X1 end,
213    false = false = begin (X2 = false) andalso X2, X2 end,
214
215    ok.
216
217t_orelse(Config) when is_list(Config) ->
218    Bs = [true,false],
219    Ps = [{X,Y} || X <- Bs, Y <- Bs],
220    lists:foreach(fun (P) -> t_orelse_1(P) end, Ps),
221
222    true = true orelse true,
223    true = true orelse false,
224    true = false orelse true,
225    false = false orelse false,
226
227    true = ?GUARD(true orelse true),
228    true = ?GUARD(true orelse false),
229    true = ?GUARD(false orelse true),
230    false = ?GUARD(false orelse false),
231
232    true = true orelse glurf,
233    true = true orelse exit(exit_now),
234
235    true = not id(false) orelse not id(false),
236    true = not id(false) orelse not id(true),
237    true = not id(true) orelse not id(false),
238    false = not id(true) orelse not id(true),
239
240    {'EXIT',{badarg,_}} = (catch not id(glurf) orelse id(true)),
241    {'EXIT',{badarg,_}} = (catch not id(true) orelse not id(glurf)),
242    true = id(true) orelse not id(glurf),
243    true = true orelse not id(glurf),
244
245    true = begin (X1 = true) orelse X1, X1 end,
246    false = begin (X2 = false) orelse X2, X2 end,
247
248    ok.
249
250t_andalso_1({X,Y}) ->
251    io:fwrite("~w andalso ~w: ",[X,Y]),
252    V1 = echo(X) andalso echo(Y),
253    V1 = if
254	     X andalso Y -> true;
255	     true -> false
256	 end,
257    V1 = id(X and Y).
258
259t_orelse_1({X,Y}) ->
260    io:fwrite("~w orelse ~w: ",[X,Y]),
261    V1 = echo(X) orelse echo(Y),
262    V1 = if
263	     X orelse Y -> true;
264	     true -> false
265	 end,
266    V1 = id(X or Y).
267
268inside(Config) when is_list(Config) ->
269    true = inside(-8, 1),
270    false = inside(-53.5, -879798),
271    false = inside(1.0, -879),
272    false = inside(59, -879),
273    false = inside(-11, 1.0),
274    false = inside(100, 0.2),
275    false = inside(100, 1.2),
276    false = inside(-53.5, 4),
277    false = inside(1.0, 5.3),
278    false = inside(59, 879),
279    ok.
280
281inside(Xm, Ym) ->
282    X = -10.0,
283    Y = -2.0,
284    W = 20.0,
285    H = 4.0,
286    Res = inside(Xm, Ym, X, Y, W, H),
287    Res = if
288	      X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H -> true;
289	      true -> false
290	  end,
291    case not id(Res) of
292	Outside ->
293	    Outside = if
294			  not(X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H) -> true;
295			  true -> false
296		      end
297    end,
298    {Res,Xm,Ym,X,Y,W,H} = inside_guard(Xm, Ym, X, Y, W, H),
299    io:format("~p =< ~p andalso ~p < ~p andalso ~p =< ~p andalso ~p < ~p ==> ~p",
300	      [X,Xm,Xm,X+W,Y,Ym,Ym,Y+H,Res]),
301    Res.
302
303inside(Xm, Ym, X, Y, W, H) ->
304    X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H.
305
306inside_guard(Xm, Ym, X, Y, W, H) when X =< Xm andalso Xm < X+W
307				      andalso Y =< Ym andalso Ym < Y+H ->
308    {true,Xm,Ym,X,Y,W,H};
309inside_guard(Xm, Ym, X, Y, W, H) ->
310    {false,Xm,Ym,X,Y,W,H}.
311
312overlap(Config) when is_list(Config) ->
313    true = overlap(7.0, 2.0, 8.0, 0.5),
314    true = overlap(7.0, 2.0, 8.0, 2.5),
315    true = overlap(7.0, 2.0, 5.3, 2),
316    true = overlap(7.0, 2.0, 0.0, 100.0),
317
318    false = overlap(-1, 2, -35, 0.5),
319    false = overlap(-1, 2, 777, 0.5),
320    false = overlap(-1, 2, 2, 10),
321    false = overlap(2, 10, 12, 55.3),
322    ok.
323
324overlap(Pos1, Len1, Pos2, Len2) ->
325    Res = case Pos1 of
326	      Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
327			orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) ->
328		  true;
329	      Pos1 -> false
330	  end,
331    Res = (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
332	orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1),
333    Res = case Pos1 of
334	      Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
335			orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) ->
336		  true;
337	      Pos1 -> false
338	  end,
339    id(Res).
340
341
342-define(COMB(A,B,C), (A andalso B orelse C)).
343
344combined(Config) when is_list(Config) ->
345    false = comb(false, false, false),
346    true = comb(false, false, true),
347    false = comb(false, true, false),
348    true = comb(false, true, true),
349
350    false = comb(true, false, false),
351    true = comb(true, true, false),
352    true = comb(true, false, true),
353    true = comb(true, true, true),
354
355    false = comb(false, blurf, false),
356    true = comb(false, blurf, true),
357    true = comb(true, true, blurf),
358
359    false = ?COMB(false, false, false),
360    true = ?COMB(false, false, true),
361    false = ?COMB(false, true, false),
362    true = ?COMB(false, true, true),
363
364    false = ?COMB(true, false, false),
365    true = ?COMB(true, true, false),
366    true = ?COMB(true, false, true),
367    true = ?COMB(true, true, true),
368
369    false = ?COMB(false, blurf, false),
370    true = ?COMB(false, blurf, true),
371    true = ?COMB(true, true, blurf),
372
373    false = simple_comb(false, false),
374    false = simple_comb(false, true),
375    false = simple_comb(true, false),
376    true = simple_comb(true, true),
377
378    ok.
379-undef(COMB).
380
381comb(A, B, C) ->
382    Res = A andalso B orelse C,
383    Res = if
384	      A andalso B orelse C -> true;
385	      true -> false
386	  end,
387    NotRes = if
388		 not(A andalso B orelse C) -> true;
389		 true -> false
390	     end,
391    NotRes = id(not Res),
392    Res = A andalso B orelse C,
393    Res = if
394	      A andalso B orelse C -> true;
395	      true -> false
396	  end,
397    NotRes = id(not Res),
398    Res = if
399	      A andalso B orelse C -> true;
400	      true -> false
401	  end,
402    id(Res).
403
404simple_comb(A, B) ->
405    %% Use Res twice, to ensure that a careless optimization of 'not'
406    %% doesn't leave Res as a free variable.
407    Res = A andalso B,
408    _ = id(not Res),
409    Res.
410
411%% Test that a boolean expression in a case expression is properly
412%% optimized (in particular, that the error behaviour is correct).
413in_case(Config) when is_list(Config) ->
414    edge_rings = in_case_1(1, 1, 1, 1, 1),
415    not_loop = in_case_1(0.5, 1, 1, 1, 1),
416    loop = in_case_1(0.5, 0.9, 1.1, 1, 4),
417    {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, 0)),
418    {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, nan)),
419    {'EXIT',{badarg,_}} = (catch in_case_1(1, 1, 1, blurf, 1)),
420    {'EXIT',{badarith,_}} = (catch in_case_1([nan], 1, 1, 1, 1)),
421    ok.
422
423in_case_1(LenUp, LenDw, LenN, Rotation, Count) ->
424    Res = in_case_1_body(LenUp, LenDw, LenN, Rotation, Count),
425    Res = in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count),
426    Res.
427
428in_case_1_body(LenUp, LenDw, LenN, Rotation, Count) ->
429    case (LenUp/Count > 0.707) and (LenN/Count > 0.707) and
430	(abs(Rotation) > 0.707) of
431	true ->
432	    edge_rings;
433	false ->
434	    case (LenUp >= 1) or (LenDw >= 1) or
435		(LenN =< 1) or (Count < 4) of
436		true ->
437		    not_loop;
438		false ->
439		    loop
440	    end
441    end.
442
443in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count) ->
444    case (LenUp/Count > 0.707) andalso (LenN/Count > 0.707) andalso
445	(abs(Rotation) > 0.707) of
446	true -> edge_rings;
447	false when LenUp >= 1 orelse LenDw >= 1 orelse
448	LenN =< 1 orelse Count < 4 -> not_loop;
449	false -> loop
450    end.
451
452-record(state, {stack = []}).
453
454slow_compilation(_) ->
455    %% The function slow_compilation_1 used to compile very slowly.
456    ok = slow_compilation_1({a}, #state{}).
457
458slow_compilation_1(T1, #state{stack = [T2|_]})
459    when element(1, T2) == a, element(1, T1) == b, element(1, T1) == c ->
460    ok;
461slow_compilation_1(T, _)
462    when element(1, T) == a1; element(1, T) == b1; element(1, T) == c1 ->
463    ok;
464slow_compilation_1(T, _)
465    when element(1, T) == a2; element(1, T) == b2; element(1, T) == c2 ->
466    ok;
467slow_compilation_1(T, _) when element(1, T) == a ->
468    ok;
469slow_compilation_1(T, _)
470    when
471        element(1, T) == a,
472        (element(1, T) == b) and (element(1, T) == c) ->
473    ok;
474slow_compilation_1(_, T) when element(1, T) == a ->
475    ok;
476slow_compilation_1(_, T) when element(1, T) == b ->
477    ok;
478slow_compilation_1(T, _) when element(1, T) == a ->
479    ok.
480
481%% Utilities.
482
483echo(X) ->
484    io:fwrite("eval(~w); ",[X]),
485    X.
486
487id(I) -> I.
488