git » blacktide.git » master » tree

[master] / test / com / tailrecursion / blacktide / control_test.clj

(ns com.tailrecursion.blacktide.control-test
  "Source control flow and live values across state-machine transitions."
  (:require [clojure.test :refer [deftest is]]
            [com.tailrecursion.blacktide :refer [loophole setq psetq]]
            [com.tailrecursion.blacktide.backend-fixture :as backend]))

(defn record! [events tag value] (swap! events conj tag) value)

(deftest clojure-conditions-and-joins
  (doseq [condition [nil false true 0 "" []]]
    (is (= (if condition :yes :no) (loophole [] (if condition :yes :no)))))
  (is (nil? (loophole [] (if false :unused))))
  (is (= 20 (loophole [x 0] (if (setq x false) (setq x 10) (setq x 20)) x)))
  (doseq [condition [true false]]
    (is (= (if condition [10 2] [1 "changed"])
           (loophole [x 1 y 2] (if condition (setq x 10) (setq y "changed")) [x y]))))
  (is (= [3 4] (loophole [x 1] [(if true (if false 2 (setq x 3)) 9) (setq x 4)]))))

(deftest operands-survive-partial-evaluation
  (is (= [1 10 10 20]
         (loophole [x 1] [x (if true (setq x 10) 0) x (if false 0 (setq x 20))])))
  (is (= [1 :changed 9]
         (loophole [f vector x 1]
           (f x (if true (do (setq f list x 9) :changed) :unused) x))))
  (is (= [nil 2 1]
         (loophole [x 1 y 2]
           [(psetq x (if true y 99) y (if false 99 x)) x y])))
  (is (= [1 3 3]
         (loophole [x 1]
           [(let [before x after (if true (setq x 3) 4)] before) x x]))))

(deftest selected-effects-and-short-circuit-macros
  (let [events (atom [])]
    (is (= [:yes nil false 7]
           (loophole [x 1]
             [(if (record! events :test true) (record! events :yes :yes) (record! events :no :no))
              (and (setq x nil) (record! events :unused 4))
              (or (setq x false) (record! events :or false))
              (cond (setq x false) (record! events :unused 6) :else (record! events :cond 7))])))
    (is (= [:test :yes :or :cond] @events)))
  (is (= [3 3] (loophole [x nil] (when-not x (setq x 3)) [(when x x) x])))
  (is (= [9 nil] (loophole [x 9] [(if-let [y x] y :absent) (when-let [y (setq x nil)] y)]))))

(deftest throwing-branches-and-dead-source
  (let [one (ex-info "one" {}) two (ex-info "two" {})]
    (doseq [condition [true false]]
      (is (identical? (if condition one two)
                     (try (loophole [] (if condition (throw one) (throw two))) (catch Throwable e e))))))
  (let [events (atom []) error (ex-info "stop" {})]
    (is (identical? error
          (try (loophole [x 1 y 2]
                 (psetq x (record! events :first 10)
                        y (if (record! events x true) (throw error) 9))
                 (record! events :after :bad))
               (catch Throwable e e))))
    (is (= [:first 1] @events)))
  (is (= :unresolved-symbol (:kind (backend/rejection '(loophole [] (if true 1 missing))))))
  (is (= :unresolved-symbol (:kind (backend/rejection '(loophole [] (throw nil) missing)))))
  (doseq [form ['(if true) '(if true 1 2 3) '(throw) '(throw nil nil)]]
    (is (= (if (= 'if (first form)) :invalid-if :invalid-throw)
           (:kind (backend/rejection (list 'loophole [] form)))))))