Skip to content

Instantly share code, notes, and snippets.

/core.clj Secret

Created November 13, 2015 12:47
Show Gist options
  • Save anonymous/dc9f501e8c52b3a7166c to your computer and use it in GitHub Desktop.
Save anonymous/dc9f501e8c52b3a7166c to your computer and use it in GitHub Desktop.
Pulsar example
;;; src/test_pulsar/core.clj
(ns test-pulsar.core
(:require
[co.paralleluniverse.pulsar
[core :refer :all]
[actors :refer :all]]
[clojure
[walk :as walk]
[zip :as zip]])
(:refer-clojure :exclude [promise await])
(:gen-class))
;; Buggy versions.
(defsfn buggy-ping [n buggy-pong]
(if (== n 0)
(do
(! buggy-pong :finished)
(println "Buggy ping finished"))
(do
(! buggy-pong [:ping @self])
(receive
:pong (println "Buggy ping received buggy pong"))
(recur (dec n) buggy-pong))))
(defsfn buggy-pong
[]
(receive
:finished (println "Buggy pong finished")
[:ping buggy-ping]
(do
(println "Pong received ping")
(! buggy-ping :pong)
(recur))))
(defn buggy-main []
(let [a1 (spawn buggy-pong)
b1 (spawn buggy-ping 3 a1)]
(join a1)
(join b1)
:ok))
(defmacro defsfnp
"Walks through a 'defn' macroexpansion and wraps the generated fn* in
a 'suspendable!' call."
[& expr]
(-> (cons 'defn expr)
walk/macroexpand-all
zip/seq-zip
zip/down
zip/rightmost
(zip/edit (fn [loc] `(suspendable! ~loc)))
zip/root))
(defsfnp ping [n pong]
(if (== n 0)
(do
(! pong :finished)
(println "ping finished"))
(do
(! pong [:ping @self])
(receive
:pong (println "Ping received pong"))
(recur (dec n) pong))))
(defsfnp pong
[]
(receive
:finished (println "Pong finished")
[:ping ping] (do
(println "Pong received ping")
(! ping :pong)
(recur))))
(defn main []
(let [a1 (spawn pong)
b1 (spawn ping 3 a1)]
(join a1)
(join b1)
:ok))
;;; test/test_pulsar/core_test.clj
(ns test-pulsar.core-test
(:require [clojure.test :refer :all]
[test-pulsar.core :refer :all]))
;; Utilities
(defn temp-ns
"Create and return a temporary ns, using clojure.core + uses"
[& uses]
(binding [*ns* *ns*]
(in-ns (gensym))
(apply clojure.core/use 'clojure.core uses)
*ns*))
(defmacro eval-in-temp-ns [& forms]
`(binding [*ns* *ns*]
(in-ns (gensym))
(clojure.core/use 'clojure.core)
(eval
'(do ~@forms))))
(defn causes
[^Throwable throwable]
(loop [causes []
t throwable]
(if t (recur (conj causes t) (.getCause t)) causes)))
(defmethod assert-expr 'fails-with-cause?
[msg [_ exception-class msg-re & body :as form]]
`(try
~@body
(report {:type :fail, :message ~msg, :expected '~form, :actual nil})
(catch Throwable t#
(if (some (fn [cause#]
(and
(= ~exception-class (class cause#))
(re-find ~msg-re (.getMessage cause#))))
(causes t#))
(report {:type :pass, :message ~msg,
:expected '~form, :actual t#})
(report {:type :fail, :message ~msg,
:expected '~form, :actual t#})))))
(defmacro with-err-print-writer
"Evaluate with err pointing to a temporary PrintWriter, and
return err contents as a string."
[& body]
`(let [s# (java.io.StringWriter.)
p# (java.io.PrintWriter. s#)]
(binding [*err* p#]
~@body
(str s#))))
(deftest defn-error-messages
(testing "multiarity syntax invalid parameter declaration"
(is (fails-with-cause?
IllegalArgumentException
#"Parameter declaration \"arg1\" should be a vector"
(eval-in-temp-ns (test-pulsar.core/defsfnp foo (arg1 arg2))))))
(testing "multiarity syntax invalid signature"
(is (fails-with-cause?
IllegalArgumentException
#"Invalid signature \"\[a b\]\" should be a list"
(eval-in-temp-ns (test-pulsar.core/defsfnp foo
([a] 1)
[a b])))))
(testing "assume single arity syntax"
(is (fails-with-cause?
IllegalArgumentException
#"Parameter declaration \"a\" should be a vector"
(eval-in-temp-ns (test-pulsar.core/defsfnp foo a)))))
(testing "bad name"
(is (fails-with-cause?
IllegalArgumentException
#"First argument to defn must be a symbol"
(eval-in-temp-ns (test-pulsar.core/defsfnp "bad docstring" testname [arg1 arg2])))))
(testing "missing parameter/signature"
(is (fails-with-cause?
IllegalArgumentException
#"Parameter declaration missing"
(eval-in-temp-ns (test-pulsar.core/defsfnp testname)))))
(testing "allow trailing map"
(is (eval-in-temp-ns (test-pulsar.core/defsfnp a "asdf" ([a] 1) {:a :b}))))
(testing "don't allow interleaved map"
(is (fails-with-cause?
IllegalArgumentException
#"Invalid signature \"\{:a :b\}\" should be a list"
(eval-in-temp-ns (test-pulsar.core/defsfnp a "asdf" ([a] 1) {:a :b} ([] 1)))))))
(deftest non-dynamic-warnings
(testing "no warning for **"
(is (empty? (with-err-print-writer
(eval-in-temp-ns (test-pulsar.core/defsfnp ** ([a b] (Math/pow (double a) (double b)))))))))
(testing "warning for *hello*"
(is (not (empty? (with-err-print-writer
(eval-in-temp-ns (test-pulsar.core/defsfnp *hello* [] "hi"))))))))
(deftest dynamic-redefinition
;; too many contextual things for this kind of caching to work...
(testing "classes are never cached, even if their bodies are the same"
(is (= :b
(eval
'(do
(defmacro my-macro [] :a)
(test-pulsar.core/defsfnp do-macro [] (my-macro))
(defmacro my-macro [] :b)
(test-pulsar.core/defsfnp do-macro [] (my-macro))
(do-macro)))))))
(deftest nested-dynamic-declaration
(testing "vars :dynamic meta data is applied immediately to vars declared anywhere"
(is (= 10
(eval
'(do
(list
(declare ^:dynamic p)
(test-pulsar.core/defsfnp q [] @p))
(binding [p (atom 10)]
(q))))))))
(defproject test-pulsar "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.7.0"]
[co.paralleluniverse/pulsar "0.7.3"]]
:java-agents [[co.paralleluniverse/quasar-core "0.7.3" :classifier "jdk8"]]
:main ^:skip-aot test-pulsar.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment