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,false} = t_case_f1(true, pos), 70 {false,true} = t_case_f1(true, whatever), 71 {false,true} = t_case_f1(false, pos), 72 {false,true} = t_case_f1(false, whatever), 73 {false,false} = t_case_f1(not_boolean, pos), 74 {false,false} = t_case_f1(not_boolean, whatever), 75 76 false = t_case_f2(true), 77 true = t_case_f2(false), 78 false = t_case_f2(whatever), 79 80 true = t_case_xy(42, 100, 700), 81 true = t_case_xy(42, 100, whatever), 82 false = t_case_xy(42, wrong, 700), 83 false = t_case_xy(42, wrong, whatever), 84 85 true = t_case_xy(0, whatever, 700), 86 true = t_case_xy(0, 100, 700), 87 false = t_case_xy(0, whatever, wrong), 88 false = t_case_xy(0, 100, wrong), 89 90 ok. 91 92t_case_a(A, B) -> 93 case A < B of 94 [_|_] -> ok; 95 true -> less; 96 false -> not_less; 97 {a,b,c} -> ok; 98 _Var -> ok 99 end. 100 101t_case_b(A, B) -> 102 case A =:= B of 103 blurf -> ok 104 end. 105 106t_case_c(A, B) -> 107 case not(A =:= B) of 108 true -> ne; 109 false -> eq 110 end. 111 112t_case_d(A, B, X) -> 113 case (A =:= B) and X of 114 true -> t; 115 false -> f 116 end. 117 118t_case_e(A, B) -> 119 case A =:= B of 120 Bool when is_tuple(A) -> id(Bool) 121 end. 122 123t_case_f1(IsInt, Eval) -> 124 B = case IsInt of 125 true -> Eval =:= pos; 126 false -> false; 127 _ -> IsInt 128 end, 129 130 %% The above is the same as `IsInt andalso Eval =:= pos` in a guard. 131 %% In a real guard, variable `B` will only be used once. 132 {B =:= true, B =:= false}. 133 134t_case_f2(IsInt) -> 135 B = case IsInt of 136 true -> false; 137 false -> true; 138 _ -> IsInt 139 end, 140 B =:= true. 141 142t_case_xy(X, Y, Z) -> 143 Res = t_case_x(X, Y, Z), 144 Res = t_case_y(X, Y, Z). 145 146t_case_x(X, Y, Z) -> 147 case abs(X) =:= 42 of 148 true -> 149 Y =:= 100; 150 false -> 151 Z =:= 700 152 end. 153 154t_case_y(X, Y, Z) -> 155 case abs(X) =:= 42 of 156 false -> 157 Z =:= 700; 158 true -> 159 Y =:= 100 160 end. 161 162-define(GUARD(E), if E -> true; 163 true -> false 164 end). 165 166t_and_or(Config) when is_list(Config) -> 167 true = true and true, 168 false = true and false, 169 false = false and true, 170 false = false and false, 171 172 true = id(true) and true, 173 false = id(true) and false, 174 false = id(false) and true, 175 false = id(false) and false, 176 177 true = true and id(true), 178 false = true and id(false), 179 false = false and id(true), 180 false = false and id(false), 181 182 true = true or true, 183 true = true or false, 184 true = false or true, 185 false = false or false, 186 187 true = id(true) or true, 188 true = id(true) or false, 189 true = id(false) or true, 190 false = id(false) or false, 191 192 true = true or id(true), 193 true = true or id(false), 194 true = false or id(true), 195 false = false or id(false), 196 197 True = id(true), 198 199 false = ?GUARD(erlang:'and'(bar, True)), 200 false = ?GUARD(erlang:'or'(bar, True)), 201 false = ?GUARD(erlang:'not'(erlang:'and'(bar, True))), 202 false = ?GUARD(erlang:'not'(erlang:'not'(erlang:'and'(bar, True)))), 203 204 true = (fun (X = true) when X or true or X -> true end)(True), 205 206 Tuple = id({a,b}), 207 case Tuple of 208 {_,_} -> 209 {'EXIT',{badarg,_}} = (catch true and Tuple) 210 end, 211 212 ok. 213 214t_andalso(Config) when is_list(Config) -> 215 Bs = [true,false], 216 Ps = [{X,Y} || X <- Bs, Y <- Bs], 217 lists:foreach(fun (P) -> t_andalso_1(P) end, Ps), 218 219 true = true andalso true, 220 false = true andalso false, 221 false = false andalso true, 222 false = false andalso false, 223 224 true = ?GUARD(true andalso true), 225 false = ?GUARD(true andalso false), 226 false = ?GUARD(false andalso true), 227 false = ?GUARD(false andalso false), 228 229 false = false andalso glurf, 230 false = false andalso exit(exit_now), 231 232 true = not id(false) andalso not id(false), 233 false = not id(false) andalso not id(true), 234 false = not id(true) andalso not id(false), 235 false = not id(true) andalso not id(true), 236 237 {'EXIT',{badarg,_}} = (catch not id(glurf) andalso id(true)), 238 {'EXIT',{badarg,_}} = (catch not id(false) andalso not id(glurf)), 239 false = id(false) andalso not id(glurf), 240 false = false andalso not id(glurf), 241 242 true = begin (X1 = true) andalso X1, X1 end, 243 false = false = begin (X2 = false) andalso X2, X2 end, 244 245 %% Cover conversion to right associativity. 246 true = (is_list(Config) andalso is_list(Bs)) andalso is_list(Ps), 247 248 ok. 249 250t_orelse(Config) when is_list(Config) -> 251 Bs = [true,false], 252 Ps = [{X,Y} || X <- Bs, Y <- Bs], 253 lists:foreach(fun (P) -> t_orelse_1(P) end, Ps), 254 255 true = true orelse true, 256 true = true orelse false, 257 true = false orelse true, 258 false = false orelse false, 259 260 true = ?GUARD(true orelse true), 261 true = ?GUARD(true orelse false), 262 true = ?GUARD(false orelse true), 263 false = ?GUARD(false orelse false), 264 265 true = true orelse glurf, 266 true = true orelse exit(exit_now), 267 268 true = not id(false) orelse not id(false), 269 true = not id(false) orelse not id(true), 270 true = not id(true) orelse not id(false), 271 false = not id(true) orelse not id(true), 272 273 {'EXIT',{badarg,_}} = (catch not id(glurf) orelse id(true)), 274 {'EXIT',{badarg,_}} = (catch not id(true) orelse not id(glurf)), 275 true = id(true) orelse not id(glurf), 276 true = true orelse not id(glurf), 277 278 true = begin (X1 = true) orelse X1, X1 end, 279 false = begin (X2 = false) orelse X2, X2 end, 280 281 %% Cover conversion to right associativity. 282 false = (is_atom(Config) orelse is_atom(Bs)) orelse is_atom(Ps), 283 284 ok. 285 286t_andalso_1({X,Y}) -> 287 io:fwrite("~w andalso ~w: ",[X,Y]), 288 V1 = echo(X) andalso echo(Y), 289 V1 = if 290 X andalso Y -> true; 291 true -> false 292 end, 293 V1 = id(X and Y). 294 295t_orelse_1({X,Y}) -> 296 io:fwrite("~w orelse ~w: ",[X,Y]), 297 V1 = echo(X) orelse echo(Y), 298 V1 = if 299 X orelse Y -> true; 300 true -> false 301 end, 302 V1 = id(X or Y). 303 304inside(Config) when is_list(Config) -> 305 true = inside(-8, 1), 306 false = inside(-53.5, -879798), 307 false = inside(1.0, -879), 308 false = inside(59, -879), 309 false = inside(-11, 1.0), 310 false = inside(100, 0.2), 311 false = inside(100, 1.2), 312 false = inside(-53.5, 4), 313 false = inside(1.0, 5.3), 314 false = inside(59, 879), 315 ok. 316 317inside(Xm, Ym) -> 318 X = -10.0, 319 Y = -2.0, 320 W = 20.0, 321 H = 4.0, 322 Res = inside(Xm, Ym, X, Y, W, H), 323 Res = if 324 X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H -> true; 325 true -> false 326 end, 327 case not id(Res) of 328 Outside -> 329 Outside = if 330 not(X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H) -> true; 331 true -> false 332 end 333 end, 334 {Res,Xm,Ym,X,Y,W,H} = inside_guard(Xm, Ym, X, Y, W, H), 335 io:format("~p =< ~p andalso ~p < ~p andalso ~p =< ~p andalso ~p < ~p ==> ~p", 336 [X,Xm,Xm,X+W,Y,Ym,Ym,Y+H,Res]), 337 Res. 338 339inside(Xm, Ym, X, Y, W, H) -> 340 X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H. 341 342inside_guard(Xm, Ym, X, Y, W, H) when X =< Xm andalso Xm < X+W 343 andalso Y =< Ym andalso Ym < Y+H -> 344 {true,Xm,Ym,X,Y,W,H}; 345inside_guard(Xm, Ym, X, Y, W, H) -> 346 {false,Xm,Ym,X,Y,W,H}. 347 348overlap(Config) when is_list(Config) -> 349 true = overlap(7.0, 2.0, 8.0, 0.5), 350 true = overlap(7.0, 2.0, 8.0, 2.5), 351 true = overlap(7.0, 2.0, 5.3, 2), 352 true = overlap(7.0, 2.0, 0.0, 100.0), 353 354 false = overlap(-1, 2, -35, 0.5), 355 false = overlap(-1, 2, 777, 0.5), 356 false = overlap(-1, 2, 2, 10), 357 false = overlap(2, 10, 12, 55.3), 358 ok. 359 360overlap(Pos1, Len1, Pos2, Len2) -> 361 Res = case Pos1 of 362 Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2) 363 orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) -> 364 true; 365 Pos1 -> false 366 end, 367 Res = (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2) 368 orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1), 369 Res = case Pos1 of 370 Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2) 371 orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) -> 372 true; 373 Pos1 -> false 374 end, 375 id(Res). 376 377 378-define(COMB(A,B,C), (A andalso B orelse C)). 379 380combined(Config) when is_list(Config) -> 381 false = comb(false, false, false), 382 true = comb(false, false, true), 383 false = comb(false, true, false), 384 true = comb(false, true, true), 385 386 false = comb(true, false, false), 387 true = comb(true, true, false), 388 true = comb(true, false, true), 389 true = comb(true, true, true), 390 391 false = comb(false, blurf, false), 392 true = comb(false, blurf, true), 393 true = comb(true, true, blurf), 394 395 false = ?COMB(false, false, false), 396 true = ?COMB(false, false, true), 397 false = ?COMB(false, true, false), 398 true = ?COMB(false, true, true), 399 400 false = ?COMB(true, false, false), 401 true = ?COMB(true, true, false), 402 true = ?COMB(true, false, true), 403 true = ?COMB(true, true, true), 404 405 false = ?COMB(false, blurf, false), 406 true = ?COMB(false, blurf, true), 407 true = ?COMB(true, true, blurf), 408 409 false = simple_comb(false, false), 410 false = simple_comb(false, true), 411 false = simple_comb(true, false), 412 true = simple_comb(true, true), 413 414 ok. 415-undef(COMB). 416 417comb(A, B, C) -> 418 Res = A andalso B orelse C, 419 Res = if 420 A andalso B orelse C -> true; 421 true -> false 422 end, 423 NotRes = if 424 not(A andalso B orelse C) -> true; 425 true -> false 426 end, 427 NotRes = id(not Res), 428 Res = A andalso B orelse C, 429 Res = if 430 A andalso B orelse C -> true; 431 true -> false 432 end, 433 NotRes = id(not Res), 434 Res = if 435 A andalso B orelse C -> true; 436 true -> false 437 end, 438 id(Res). 439 440simple_comb(A, B) -> 441 %% Use Res twice, to ensure that a careless optimization of 'not' 442 %% doesn't leave Res as a free variable. 443 Res = A andalso B, 444 _ = id(not Res), 445 Res. 446 447%% Test that a boolean expression in a case expression is properly 448%% optimized (in particular, that the error behaviour is correct). 449in_case(Config) when is_list(Config) -> 450 edge_rings = in_case_1(1, 1, 1, 1, 1), 451 not_loop = in_case_1(0.5, 1, 1, 1, 1), 452 loop = in_case_1(0.5, 0.9, 1.1, 1, 4), 453 {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, 0)), 454 {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, nan)), 455 {'EXIT',{badarg,_}} = (catch in_case_1(1, 1, 1, blurf, 1)), 456 {'EXIT',{badarith,_}} = (catch in_case_1([nan], 1, 1, 1, 1)), 457 ok. 458 459in_case_1(LenUp, LenDw, LenN, Rotation, Count) -> 460 Res = in_case_1_body(LenUp, LenDw, LenN, Rotation, Count), 461 Res = in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count), 462 Res. 463 464in_case_1_body(LenUp, LenDw, LenN, Rotation, Count) -> 465 case (LenUp/Count > 0.707) and (LenN/Count > 0.707) and 466 (abs(Rotation) > 0.707) of 467 true -> 468 edge_rings; 469 false -> 470 case (LenUp >= 1) or (LenDw >= 1) or 471 (LenN =< 1) or (Count < 4) of 472 true -> 473 not_loop; 474 false -> 475 loop 476 end 477 end. 478 479in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count) -> 480 case (LenUp/Count > 0.707) andalso (LenN/Count > 0.707) andalso 481 (abs(Rotation) > 0.707) of 482 true -> edge_rings; 483 false when LenUp >= 1 orelse LenDw >= 1 orelse 484 LenN =< 1 orelse Count < 4 -> not_loop; 485 false -> loop 486 end. 487 488-record(state, {stack = []}). 489 490slow_compilation(_) -> 491 %% The function slow_compilation_1 used to compile very slowly. 492 ok = slow_compilation_1({a}, #state{}). 493 494slow_compilation_1(T1, #state{stack = [T2|_]}) 495 when element(1, T2) == a, element(1, T1) == b, element(1, T1) == c -> 496 ok; 497slow_compilation_1(T, _) 498 when element(1, T) == a1; element(1, T) == b1; element(1, T) == c1 -> 499 ok; 500slow_compilation_1(T, _) 501 when element(1, T) == a2; element(1, T) == b2; element(1, T) == c2 -> 502 ok; 503slow_compilation_1(T, _) when element(1, T) == a -> 504 ok; 505slow_compilation_1(T, _) 506 when 507 element(1, T) == a, 508 (element(1, T) == b) and (element(1, T) == c) -> 509 ok; 510slow_compilation_1(_, T) when element(1, T) == a -> 511 ok; 512slow_compilation_1(_, T) when element(1, T) == b -> 513 ok; 514slow_compilation_1(T, _) when element(1, T) == a -> 515 ok. 516 517%% Utilities. 518 519echo(X) -> 520 io:fwrite("eval(~w); ",[X]), 521 X. 522 523id(I) -> I. 524