291 lines
7.2 KiB
Hy
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])))
|