Current section

Files

Jump to
lfe test andor_SUITE.lfe
Raw

test/andor_SUITE.lfe

;; Copyright (c) 2008-2020 Robert Virding
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; File : andor_SUITE.lfe
;; Author : Robert Virding
;; Purpose : Guard test suite.
;; This is a direct translation of andor_SUITE.erl from R14B02 except
;; for tests with guards containing ';'. We have usually removed these
;; or been careful with these as they don't handle errors the same way
;; as 'or' (which is all we have).
;;
;; Note that some of these tests are not LFE specific but more general
;; guard tests but we include them anyway for completeness.
(include-file "test_server.lfe")
(defmodule andor_SUITE
(export (all 0) (suite 0) (groups 0) (init_per_suite 1) (end_per_suite 1)
(init_per_group 2) (end_per_group 2)
(t_case 1) (t_and_or 1) (t_andalso 1) (t_orelse 1) (inside 1)
(overlap 1) (combined 1) (in_case 1) (before_and_inside_if 1)
))
(defmacro MODULE () `'andor_SUITE)
(defun all ()
;; (: test_lib recompile (MODULE))
(list 't_case 't_and_or 't_andalso 't_orelse
'inside 'overlap 'combined 'in_case 'before_and_inside_if))
;;(defun suite () (list (tuple 'ct_hooks (list 'ts_install_cth))))
(defun suite () ())
(defun groups () ())
(defun init_per_suite (config) config)
(defun end_per_suite (config) 'ok)
(defun init_per_group (name config) config)
(defun end_per_group (name config) config)
(defun t_case
(['suite] ())
(['doc] '"Test in case.")
([config] (when (is_list config))
;; We test boolean cases almost but not quite like cases
;; generated by andalso/orelse.
(line (test-pat 'less (t-case-a 1 2)))
(line (test-pat 'not_less (t-case-a 2 2)))
(line (test-pat (tuple 'EXIT (tuple (tuple 'case_clause 'false) _))
(catch (t-case-b #(x y z) 2))))
(line (test-pat (tuple 'EXIT (tuple (tuple 'case_clause 'true) _))
(catch (t-case-b 'a 'a))))
(line (test-pat 'eq (t-case-c 'a 'a)))
(line (test-pat 'ne (t-case-c 42 ())))
(line (test-pat 't (t-case-d 'x 'x 'true)))
(line (test-pat 'f (t-case-d 'x 'x 'false)))
(line (test-pat 'f (t-case-d 'x 'y 'true)))
(line (test-pat (tuple 'EXIT (tuple 'badarg _))
(catch (t-case-d 'x 'y 'blurf))))
(line (test-pat 'true (catch (t-case-e #(a b) #(a b)))))
(line (test-pat 'false (catch (t-case-e #(a b) 42))))
(line (test-pat 'true (t-case-xy 42 100 700)))
(line (test-pat 'true (t-case-xy 42 100 'whatever)))
(line (test-pat 'false (t-case-xy 42 'wrong 700)))
(line (test-pat 'false (t-case-xy 42 'wrong 'whatever)))
(line (test-pat 'true (t-case-xy 0 'whatever 700)))
(line (test-pat 'true (t-case-xy 0 100 700)))
(line (test-pat 'false (t-case-xy 0 'whatever 'wrong)))
(line (test-pat 'false (t-case-xy 0 100 'wrong)))
'ok))
(defun t-case-a (a b)
(case (< a b)
((cons _ _) 'ok)
('true 'less)
('false 'not_less)
(#(a b c) 'ok)
(_ 'ok)))
(defun t-case-b (a b)
(case (=:= a b)
('blurf 'ok)))
(defun t-case-c (a b)
(case (not (=:= a b))
('true 'ne)
('false 'eq)))
(defun t-case-d (a b x)
(case (and (=:= a b) x)
('true 't)
('false 'f)))
(defun t-case-e (a b)
(case (=:= a b)
(bool (when (is_tuple a)) (id bool))))
(defun t-case-xy (x y z)
(let* ((r0 (t-case-x x y z))
(res (when (=:= res r0)) (t-case-y x y z)))
res))
(defun t-case-x (x y z)
(case (=:= (abs x) 42)
('true (=:= y 100))
('false (=:= z 700))))
(defun t-case-y (x y z)
(case (=:= (abs x) 42)
('false (=:= z 700))
('true (=:= y 100))))
(defun t_and_or
([config] (when (is_list config))
(line (test-pat 'true (and 'true 'true)))
(line (test-pat 'false (and 'true 'false)))
(line (test-pat 'false (and 'false 'true)))
(line (test-pat 'false (and 'false 'false)))
(line (test-pat 'true (and (id 'true) 'true)))
(line (test-pat 'false (and (id 'true) 'false)))
(line (test-pat 'false (and (id 'false) 'true)))
(line (test-pat 'false (and (id 'false) 'false)))
(line (test-pat 'true (and 'true (id 'true))))
(line (test-pat 'false (and 'true (id 'false))))
(line (test-pat 'false (and 'false (id 'true))))
(line (test-pat 'false (and 'false (id 'false))))
(line (test-pat 'true (or 'true 'true)))
(line (test-pat 'true (or 'true 'false)))
(line (test-pat 'true (or 'false 'true)))
(line (test-pat 'false (or 'false 'false)))
(line (test-pat 'true (or (id 'true) 'true)))
(line (test-pat 'true (or (id 'true) 'false)))
(line (test-pat 'true (or (id 'false) 'true)))
(line (test-pat 'false (or (id 'false) 'false)))
(line (test-pat 'true (or 'true (id 'true))))
(line (test-pat 'true (or 'true (id 'false))))
(line (test-pat 'true (or 'false (id 'true))))
(line (test-pat 'false (or 'false (id 'false))))
'ok))
(defmacro GUARD (t)
`(eif ,t 'true 'true 'false))
(defun t_andalso
([config] (when (is_list config))
(let* ((bs '(true false))
(ps (lc ((<- x bs) (<- y bs))
(tuple x y))))
(: lists foreach (lambda (p) (t-andalso-1 p)) ps))
(line (test-pat 'true (andalso 'true 'true)))
(line (test-pat 'false (andalso 'true 'false)))
(line (test-pat 'false (andalso 'false 'true)))
(line (test-pat 'false (andalso 'false 'false)))
(line (test-pat 'true (GUARD (andalso 'true 'true))))
(line (test-pat 'false (GUARD (andalso 'true 'false))))
(line (test-pat 'false (GUARD (andalso 'false 'true))))
(line (test-pat 'false (GUARD (andalso 'false 'false))))
(line (test-pat 'false (andalso 'false 'glurf)))
(line (test-pat 'false (andalso 'false (exit 'exit_now))))
(line (test-pat 'true (andalso (not (id 'false)) (not (id 'false)))))
(line (test-pat 'false (andalso (not (id 'false)) (not (id 'true)))))
(line (test-pat 'false (andalso (not (id 'true)) (not (id 'false)))))
(line (test-pat 'false (andalso (not (id 'true)) (not (id 'true)))))
(line (test-pat (tuple 'EXIT (tuple 'badarg _))
(catch (andalso (not (id 'glurf)) (id 'true)))))
(line (test-pat (tuple 'EXIT (tuple 'badarg _))
(catch (andalso (not (id 'false)) (not (id 'glurf))))))
(line (test-pat 'false (andalso (id 'false) (not (id 'glurf)))))
(line (test-pat 'false (andalso 'false (not (id 'glurf)))))
'ok))
(defun t-andalso-1
([(tuple x y)]
(: lfe_io format '"(andalso ~w ~w): " (list x y))
(let* ((v0 (andalso (echo x) (echo y)))
(v1 (when (=:= v0 v1))
(eif (andalso x y) 'true 'true 'false)))
(check v1 (and x y)))))
(defun t_orelse
([config] (when (is_list config))
(let* ((bs '(true false))
(ps (lc ((<- x bs) (<- y bs)) (tuple x y))))
(: lists foreach (lambda (p) (t-orelse-1 p)) ps))
(line (test-pat 'true (orelse 'true 'true)))
(line (test-pat 'true (orelse 'true 'false)))
(line (test-pat 'true (orelse 'false 'true)))
(line (test-pat 'false (orelse 'false 'false)))
(line (test-pat 'true (GUARD (orelse 'true 'true))))
(line (test-pat 'true (GUARD (orelse 'true 'false))))
(line (test-pat 'true (GUARD (orelse 'false 'true))))
(line (test-pat 'false (GUARD (orelse 'false 'false))))
(line (test-pat 'true (orelse 'true 'glurf)))
(line (test-pat 'true (orelse 'true (exit 'exit_now))))
(line (test-pat 'true (orelse (not (id 'false)) (not (id 'false)))))
(line (test-pat 'true (orelse (not (id 'false)) (not (id 'true)))))
(line (test-pat 'true (orelse (not (id 'true)) (not (id 'false)))))
(line (test-pat 'false (orelse (not (id 'true)) (not (id 'true)))))
(line (test-pat (tuple 'EXIT (tuple 'badarg _))
(catch (orelse (not (id 'glurf)) (id 'true)))))
(line (test-pat (tuple 'EXIT (tuple 'badarg _))
(catch (orelse (not (id 'true)) (not (id 'glurf))))))
(line (test-pat 'true (orelse (id 'true) (not (id 'glurf)))))
(line (test-pat 'true (orelse 'true (not (id 'glurf)))))
'ok))
(defun t-orelse-1
([(tuple x y)]
(: lfe_io format '"(orelse ~w ~w): " (list x y))
(let* ((v0 (orelse (echo x) (echo y)))
(v1 (when (=:= v0 v1))
(eif (orelse x y) 'true 'true 'false)))
(check v1 (or x y)))))
(defun inside
([config] (when (is_list config))
(line (test-pat 'true (inside -8 1)))
(line (test-pat 'false (inside -53.5 -879798)))
(line (test-pat 'false (inside 1.0 -879)))
(line (test-pat 'false (inside 59 -879)))
(line (test-pat 'false (inside -11 1.0)))
(line (test-pat 'false (inside 100 0.2)))
(line (test-pat 'false (inside 100 1.2)))
(line (test-pat 'false (inside -53.5 4)))
(line (test-pat 'false (inside 1.0 5.3)))
(line (test-pat 'false (inside 59 879)))
'ok))
(defun inside (xm ym)
(let* ((x -10.0)
(y -2.0)
(w 20.0)
(h 4.0)
(r0 (inside xm ym x y w h))
(r1 (when (=:= r0 r1))
(eif (andalso (=< x xm) (< xm (+ x w)) (=< y ym) (< ym (+ y h)))
'true 'true 'false)))
(case (not (id r1))
(o0
(let ((o1 (when (=:= o0 o1))
(eif (not (andalso (=< x xm) (< xm (+ x w))
(=< y ym) (< ym (+ y h))))
'true 'true 'false)))
o1)))
(let (((tuple r2 xm2 ym2 x2 y2 w2 h2)
(when (=:= r1 r2) (=:= xm xm2) (=:= ym ym2) (=:= x x2)
(=:= y y2) (=:= w w2) (=:= h h2))
(inside-guard xm ym x y w h)))
(: lfe_io fwrite
'"(andalso (=< ~p ~p) (< ~p ~p) (=< ~p ~p) (< ~p ~p)) ==> ~p\n"
(list x xm xm (+ x w) y ym ym (+ y h) r1)))
r1))
(defun inside (xm ym x y w h)
(andalso (=< x xm) (< xm (+ x w)) (=< y ym) (< ym (+ y h))))
(defun inside-guard
([xm ym x y w h]
(when (andalso (=< x xm) (< xm (+ x w)) (=< y ym) (< ym (+ y h))))
(tuple 'true xm ym x y w h))
([xm ym x y w h]
(tuple 'false xm ym x y w h)))
(defun overlap
([config] (when (is_list config))
(line (test-pat 'true (overlap 7.0 2.0 8.0 0.5)))
(line (test-pat 'true (overlap 7.0 2.0 8.0 2.5)))
(line (test-pat 'true (overlap 7.0 2.0 5.3 2)))
(line (test-pat 'true (overlap 7.0 2.0 0.0 100.0)))
(line (test-pat 'false (overlap -1 2 -35 0.5)))
(line (test-pat 'false (overlap -1 2 777 0.5)))
(line (test-pat 'false (overlap -1 2 2 10)))
(line (test-pat 'false (overlap 2 10 12 55.3)))
'ok))
(defun overlap (pos1 len1 pos2 len2)
(let* ((r0 (case pos1
(pos1 (when (orelse (andalso (=< pos2 pos1)
(< pos1 (+ pos2 len2)))
(andalso (=< pos1 pos2)
(< pos2 (+ pos1 len1)))))
'true)
(pos1 'false)))
(r1 (when (=:= r0 r1))
(orelse (andalso (=< pos2 pos1)
(< pos1 (+ pos2 len2)))
(andalso (=< pos1 pos2)
(< pos2 (+ pos1 len1)))))
(r2 (when (=:= r2 r1))
(case pos1
(pos1 (when (orelse (andalso (=< pos2 pos1)
(< pos1 (+ pos2 len2)))
(andalso (=< pos1 pos2)
(< pos2 (+ pos1 len1)))))
'true)
(pos1 'false))))
(id r2)))
;; -define(COMB(A,B,C), (A andalso B orelse C)).
(defmacro COMB (a b c)
`(orelse (andalso ,a ,b) ,c))
(defun combined
([config] (when (is_list config))
(line (test-pat 'false (comb 'false 'false 'false)))
(line (test-pat 'true (comb 'false 'false 'true)))
(line (test-pat 'false (comb 'false 'true 'false)))
(line (test-pat 'true (comb 'false 'true 'true)))
(line (test-pat 'false (comb 'true 'false 'false)))
(line (test-pat 'true (comb 'true 'true 'false)))
(line (test-pat 'true (comb 'true 'false 'true)))
(line (test-pat 'true (comb 'true 'true 'true)))
(line (test-pat 'false (comb 'false 'blurf 'false)))
(line (test-pat 'true (comb 'false 'blurf 'true)))
(line (test-pat 'true (comb 'true 'true 'blurf)))
(line (test-pat 'false (COMB 'false 'false 'false)))
(line (test-pat 'true (COMB 'false 'false 'true)))
(line (test-pat 'false (COMB 'false 'true 'false)))
(line (test-pat 'true (COMB 'false 'true 'true)))
(line (test-pat 'false (COMB 'true 'false 'false)))
(line (test-pat 'true (COMB 'true 'true 'false)))
(line (test-pat 'true (COMB 'true 'false 'true)))
(line (test-pat 'true (COMB 'true 'true 'true)))
;; This next one crashed the compiler!
(line (test-pat 'false (COMB 'false 'blurf 'false)))
(line (test-pat 'true (COMB 'false 'blurf 'true)))
(line (test-pat 'true (COMB 'true 'true 'blurf)))
(test-pat 'false (simple-comb 'false 'false))
(test-pat 'false (simple-comb 'false 'true))
(test-pat 'false (simple-comb 'true 'false))
(test-pat 'true (simple-comb 'true 'true))
'ok))
(defun comb (a b c)
(let* ((r0 (orelse (andalso a b) c))
(r1 (when (=:= r0 r1))
(eif (orelse (andalso a b) c) 'true 'true 'false))
(n0 (eif (not (orelse (andalso a b) c)) 'true 'true 'false))
(n1 (when (=:= n0 n1))
(id (not r1)))
(r2 (when (=:= r1 r2))
(orelse (andalso a b) c))
(r3 (when (=:= r2 r3))
(eif (orelse (andalso a b) c) 'true 'true 'false))
(n2 (when (=:= n1 n2))
(id (not r3)))
(r4 (when (=:= r3 r4))
(eif (orelse (andalso a b) c) 'true 'true 'false)))
(id r4)))
(defun simple-comb (a b)
;; Use res twice, to ensure that a careless optimization of 'not'
;; doesn't leave res as a free variable.
(let* ((res (andalso a b))
(_ (id res)))
res))
;; Test that a boolean expression in a case expression is properly
;; optimized (in particular, that the error behaviour is correct).
(defun in_case
([config] (when (is_list config))
(line (test-pat 'edge_rings (in-case-1 1 1 1 1 1)))
(line (test-pat 'not_loop (in-case-1 0.5 1 1 1 1)))
(line (test-pat 'loop (in-case-1 0.5 0.9 1.1 1 4)))
(line (test-pat (tuple 'EXIT (tuple 'badarith _))
(catch (in-case-1 1 1 1 1 0))))
(line (test-pat (tuple 'EXIT (tuple 'badarith _))
(catch (in-case-1 1 1 1 1 'nan))))
(line (test-pat (tuple 'EXIT (tuple 'badarg _))
(catch (in-case-1 1 1 1 'blurf 1))))
(line (test-pat (tuple 'EXIT (tuple 'badarith _))
(catch (in-case-1 '(nan) 1 1 1 1))))
'ok))
(defun in-case-1 (lenup lendw lenn rot count)
(let* ((r0 (in-case-1-body lenup lendw lenn rot count))
(res (when (=:= r0 res))
(in-case-1-guard lenup lendw lenn rot count)))
res))
(defun in-case-1-body (lenup lendw lenn rot count)
;; This is a 'cond'.
(case (and (and (> (/ lenup count) 0.707) (> (/ lenn count) 0.707))
(> (abs rot) 0.707))
('true 'edge_rings)
('false
(case (or (or (or (>= lenup 1) (>= lendw 1)) (=< lenn 1)) (> count 4))
('true 'not_loop)
('false 'loop)))))
(defun in-case-1-guard (lenup lendw lenn rot count)
(case (andalso (> (/ lenup count) 0.707) (> (/ lenn count) 0.707)
(> (abs rot) 0.707))
('true 'edge_rings)
('false (when (orelse (>= lenup 1) (>= lendw 1) (=< lenn 1) (< count 4)))
'not_loop)
('false 'loop)))
(defun before_and_inside_if
([config] (when (is_list config))
;; We don't have real if's.
(line (test-pat 'no (before-and-inside-if '(a) '(b) 'delete)))
(line (test-pat 'no (before-and-inside-if '(a) '(b) 'x)))
(line (test-pat 'no (before-and-inside-if '(a) '() 'delete)))
(line (test-pat 'no (before-and-inside-if '(a) '() 'x)))
(line (test-pat 'no (before-and-inside-if '() '() 'delete)))
(line (test-pat 'yes (before-and-inside-if '() '() 'x)))
(line (test-pat 'yes (before-and-inside-if '() '(b) 'delete)))
(line (test-pat 'yes (before-and-inside-if '() '(b) 'x)))
(line (test-pat #(ch1 ch2) (before-and-inside-if-2 '(a) '(b) 'blah)))
(line (test-pat #(ch1 ch2) (before-and-inside-if-2 '(a) '(b) 'xx)))
(line (test-pat #(ch1 ch2) (before-and-inside-if-2 '(a) '() 'blah)))
(line (test-pat #(ch1 ch2) (before-and-inside-if-2 '(a) '() 'xx)))
(line (test-pat #(no no) (before-and-inside-if-2 '() '(b) 'blah)))
(line (test-pat #(no no) (before-and-inside-if-2 '() '(b) 'xx)))
(line (test-pat #(ch1 no) (before-and-inside-if-2 '() '() 'blah)))
(line (test-pat #(no ch2) (before-and-inside-if-2 '() '() 'xx)))
'ok))
;; Thanks to Simon Cornish and Kostis Sagonas.
;; Used to crash beam_bool.
(defun before-and-inside-if (XDo1 XDo2 Do3)
(let* ((Do1 (=/= XDo1 ()))
(Do2 (=/= XDo2 ())))
(eif
;; This expression occurs in a try/catch (protected)
;; block, which cannot refer to variables outside of
;; the block that are boolean expressions.
(or (=:= Do1 'true)
(and (and (=:= Do1 'false) (=:= Do2 'false)) (=:= Do3 'delete)))
'no
'true 'yes)))
;; Thanks to Simon Cornish.
;; Used to generate code that would not set {y,0} on
;; all paths before its use (and therefore fail
;; validation by the beam_validator).
(defun before-and-inside-if-2 (XDo1 XDo2 Do3)
(let* ((Do1 (=/= XDo1 ()))
(Do2 (=/= XDo2 ()))
(CH1 (eif (or (== Do1 'true)
(and (and (== Do1 'false) (== Do2 'false))
(== Do3 'blah)))
'ch1
'true 'no))
(CH2 (eif (or (== Do1 'true)
(and (and (== Do1 'false) (== Do2 'false))
(== Do3 'xx)))
'ch2
'true 'no)))
(tuple CH1 CH2)))
;; Utilities
(defun check (v1 v0)
(eif (/= v1 v0) (progn (: lfe_io fwrite '"error: ~w.\n" (list v1))
(exit 'suite_failed))
'true (: lfe_io fwrite '"ok: ~w.\n" (list v1))))
(defun echo (x)
(: lfe_io fwrite '"(eval ~w); " (list x))
x)
;; Call this function to turn off constant propagation.
(defun id (i) i)