hy/tests/native_tests/operators.hy
2017-07-17 13:34:42 -07:00

291 lines
7.2 KiB
Hy

;; Copyright 2017 the authors.
;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE.
(import pytest [hy._compat [PY35]])
(defmacro op-and-shadow-test [op &rest body]
; Creates two tests with the given `body`, one where all occurrences
; of the symbol `f` are syntactically replaced with `op` (a test of
; the real operator), and one where the body is preceded by an
; assignment of `op` to `f` (a test of the shadow operator).
;
; `op` can also be a list of operators, in which case two tests are
; created for each operator.
(import [hy [HySymbol HyString]] [hy.contrib.walk [prewalk]])
(setv defns [])
(for [o (if (coll? op) op [op])]
(.append defns `(defn ~(HySymbol (+ "test_operator_" o "_real")) []
(setv f-name ~(HyString o))
~@(prewalk :form body :f (fn [x]
(if (and (symbol? x) (= x "f")) o x)))))
(.append defns `(defn ~(HySymbol (+ "test_operator_" o "_shadow")) []
(setv f-name ~(HyString o))
(setv f ~o)
~@body)))
`(do ~@defns))
(defmacro forbid [expr]
`(assert (try
(eval '~expr)
(except [TypeError] True)
(else (raise AssertionError)))))
(op-and-shadow-test +
(assert (= (f) 0))
(defclass C [object] [__pos__ (fn [self] "called __pos__")])
(assert (= (f (C)) "called __pos__"))
(assert (= (f 1 2) 3))
(assert (= (f 1 2 3 4) 10))
(assert (= (f 1 2 3 4 5) 15))
; with strings
(assert (= (f "a" "b" "c")
"abc"))
; with lists
(assert (= (f ["a"] ["b"] ["c"])
["a" "b" "c"])))
(op-and-shadow-test -
(forbid (f))
(assert (= (f 1) -1))
(assert (= (f 2 1) 1))
(assert (= (f 2 1 1) 0)))
(op-and-shadow-test *
(assert (= (f) 1))
(assert (= (f 3) 3))
(assert (= (f 3 3) 9))
(assert (= (f 2 3 4) 24))
(assert (= (f "ke" 4) "kekekeke"))
(assert (= (f [1 2 3] 2) [1 2 3 1 2 3])))
(op-and-shadow-test **
(forbid (f))
(forbid (f 1))
(assert (= (f 3 2) 9))
(assert (= (f 5 4 3 2) (** 5 (** 4 (** 3 2))))))
; Exponentiation is right-associative.
(op-and-shadow-test /
(forbid (f))
(assert (= (f 2) .5))
(assert (= (f 3 2) 1.5))
(assert (= (f 8 2) 4))
(assert (= (f 8 2 2) 2))
(assert (= (f 8 2 2 2) 1)))
(op-and-shadow-test //
(forbid (f))
(forbid (f 1))
(assert (= (f 16 5) 3))
(assert (= (f 8 2) 4))
(assert (= (f 8 2 2) 2)))
(op-and-shadow-test %
(forbid (f))
(forbid (f 1))
(assert (= (f 16 5) 1))
(assert (= (f 8 2) 0))
(assert (= (f "aa %s bb" 15) "aa 15 bb"))
(assert (= (f "aa %s bb %s cc" (, "X" "Y")) "aa X bb Y cc"))
(forbid (f 1 2 3)))
(when PY35 (op-and-shadow-test @
(defclass C [object] [
__init__ (fn [self content] (setv self.content content))
__matmul__ (fn [self other] (C (+ self.content other.content)))])
(forbid (f))
(assert (do (setv c (C "a")) (is (f c) c)))
(assert (= (. (f (C "b") (C "c")) content) "bc"))
(assert (= (. (f (C "d") (C "e") (C "f")) content) "def"))))
(op-and-shadow-test <<
(forbid (f))
(forbid (f 1))
(assert (= (f 0b101 2) 0b10100))
(assert (= (f 0b101 2 3) 0b10100000)))
(op-and-shadow-test >>
(forbid (f))
(forbid (f 1))
(assert (= (f 0b101 2) 0b1))
(assert (= (f 0b101000010 2 3) 0b1010)))
(op-and-shadow-test &
(forbid (f))
; Binary AND has no identity element for the set of all
; nonnegative integers, because we can't have a 1 in every bit
; when there are infinitely many bits.
(assert (= (f 17) 17))
(assert (= (f 0b0011 0b0101) 0b0001))
(assert (= (f 0b111 0b110 0b100) 0b100)))
(op-and-shadow-test |
(assert (= (f) 0))
(assert (= (f 17) 17))
(assert (= (f 0b0011 0b0101) 0b0111))
(assert (= (f 0b11100 0b11000 0b10010) 0b11110)))
(op-and-shadow-test ^
(forbid (f))
(forbid (f 17))
(assert (= (f 0b0011 0b0101) 0b0110))
(forbid (f 0b111 0b110 0b100)))
; `xor` with 3 arguments is kill (https://github.com/hylang/hy/pull/1102),
; so we don't allow `^` with 3 arguments, either.
(op-and-shadow-test ~
(forbid (f))
(assert (= (f (chr 0b00101111)
(chr 0b11010000))))
(forbid (f (chr 0b00101111) (chr 0b11010000))))
(op-and-shadow-test <
(forbid (f))
(assert (is (f "hello") True))
(assert (is (f 1 2) True))
(assert (is (f 2 1) False))
(assert (is (f 1 1) False))
(assert (is (f 1 2 3) True))
(assert (is (f 3 2 1) False))
(assert (is (f 1 3 2) False))
(assert (is (f 1 2 2) False))
; Make sure chained comparisons use `and`, not `&`.
; https://github.com/hylang/hy/issues/1191
(defclass C [object] [
__init__ (fn [self x]
(setv self.x x))
__lt__ (fn [self other]
self.x)])
(assert (= (f (C "a") (C "b") (C "c")) "b")))
(op-and-shadow-test >
(forbid (f))
(assert (is (f "hello") True))
(assert (is (f 1 2) False))
(assert (is (f 2 1) True))
(assert (is (f 1 1) False))
(assert (is (f 1 2 3) False))
(assert (is (f 3 2 1) True))
(assert (is (f 1 3 2) False))
(assert (is (f 2 1 1) False)))
(op-and-shadow-test <=
(forbid (f))
(assert (is (f "hello") True))
(assert (is (f 1 2) True))
(assert (is (f 2 1) False))
(assert (is (f 1 1) True))
(assert (is (f 1 2 3) True))
(assert (is (f 3 2 1) False))
(assert (is (f 1 3 2) False))
(assert (is (f 1 2 2) True)))
(op-and-shadow-test >=
(forbid (f))
(assert (is (f "hello") True))
(assert (is (f 1 2) False))
(assert (is (f 2 1) True))
(assert (is (f 1 1) True))
(assert (is (f 1 2 3) False))
(assert (is (f 3 2 1) True))
(assert (is (f 1 3 2) False))
(assert (is (f 2 1 1) True)))
(op-and-shadow-test [= is]
(forbid (f))
(assert (is (f "hello") True))
(defclass C)
(setv x (get {"is" (C) "=" 0} f-name))
(setv y (get {"is" (C) "=" 1} f-name))
(assert (is (f x x) True))
(assert (is (f y y) True))
(assert (is (f x y) False))
(assert (is (f y x) False))
(assert (is (f x x x x x) True))
(assert (is (f x x x y x) False))
(setv n None)
(assert (is (f n None) True))
(assert (is (f n "b") False)))
(op-and-shadow-test [!= is-not]
(forbid (f))
(forbid (f "hello"))
(defclass C)
(setv x (get {"is_not" (C) "!=" 0} f-name))
(setv y (get {"is_not" (C) "!=" 1} f-name))
(setv z (get {"is_not" (C) "!=" 2} f-name))
(assert (is (f x x) False))
(assert (is (f y y) False))
(assert (is (f x y) True))
(assert (is (f y x) True))
(assert (is (f x y z) True))
(assert (is (f x x x) False))
(assert (is (f x y x) True))
(assert (is (f x x y) False)))
(op-and-shadow-test and
(assert (is (f) True))
(assert (= (f 17) 17))
(assert (= (f 1 2) 2))
(assert (= (f 1 0) 0))
(assert (= (f 0 2) 0))
(assert (= (f 0 0) 0))
(assert (= (f 1 2 3) 3))
(assert (= (f 1 0 3) 0))
(assert (= (f "a" 1 True [1]) [1])))
(op-and-shadow-test or
(assert (is (f) None))
(assert (= (f 17) 17))
(assert (= (f 1 2) 1))
(assert (= (f 1 0) 1))
(assert (= (f 0 2) 2))
(assert (= (f 0 0) 0))
(assert (= (f 1 2 3) 1))
(assert (= (f 0 0 3) 3))
(assert (= (f "" None 0 False []) [])))
(op-and-shadow-test not
(forbid (f))
(assert (is (f "hello") False))
(assert (is (f 0) True))
(assert (is (f None) True)))
(op-and-shadow-test [in not-in]
(forbid (f))
(forbid (f 3))
(assert (is (f 3 [1 2]) (!= f-name "in")))
(assert (is (f 2 [1 2]) (= f-name "in")))
(forbid (f 2 [1 2] [3 4])))