(.require [library [lux (.except) [abstract [monad (.only do)]] [control ["[0]" try (.only Try)] ["[0]" exception (.only exception)] ["[0]" io (.only IO io)]] [data [text ["%" \\format (.only format)]] [collection ["[0]" list] ["[0]" sequence (.only Sequence)]]] [math ["[0]" random] [number ["n" nat]]] [test ["_" property (.only Test)] ["[0]" unit]]]] [\\library ["[0]" / (.only) [// ["[0]" atom (.only Atom)] ["[0]" async (.only Async Resolver) (.use "[1]#[0]" monad)] ["[0]" frp]]]]) (exception got_wrecked) (def counter (/.Behavior Nat) (function (_ message state self) (message state self))) (def (count! increment) (-> Nat (/.Message Nat Nat)) (function (_ state self) (let [state' (n.+ increment state)] (async#in {try.#Success [state' state']})))) (def (mailed? outcome) (-> (Try Any) Bit) (when outcome {try.#Success _} true {try.#Failure _} false)) (def .public test Test (do [! random.monad] [initial_state random.nat .let [as_mail (is (All (_ a) (-> (-> a a) (/.Mail a))) (function (_ transform) (function (_ state actor) (|> state transform {try.#Success} async#in)))) ++! (is (/.Mail Nat) (as_mail ++)) --! (is (/.Mail Nat) (as_mail --))]] (<| (_.covering /._) (_.for [/.Actor]) (all _.and (_.coverage [/.alive?] (io.run! (do io.monad [actor (/.spawn! /.default 0)] (/.alive? actor)))) (_.coverage [/.poison!] (let [poisoned_actors_die! (io.run! (do io.monad [actor (/.spawn! /.default 0) poisoned? (/.poison! actor) alive? (/.alive? actor)] (in (and (..mailed? poisoned?) (not alive?))))) cannot_poison_more_than_once! (io.run! (do io.monad [actor (/.spawn! /.default 0) first_time? (/.poison! actor) second_time? (/.poison! actor)] (in (and (..mailed? first_time?) (not (..mailed? second_time?))))))] (and poisoned_actors_die! cannot_poison_more_than_once!))) (let [[read write] (is [(Async Text) (Resolver Text)] (async.async []))] (in (do async.monad [_ (async.future (do io.monad [actor (/.spawn! (is (/.Behavior Any) (function (_ message state self) (do [! async.monad] [outcome (message state self)] (when outcome {try.#Failure cause} (do ! [_ (async.future (write cause))] (in outcome)) {try.#Success _} (in outcome))))) [])] (/.poison! actor))) _ (async.delay 100) result (async.future (async.value read))] (unit.coverage [/.poisoned] (when result {.#Some error} (exception.match? /.poisoned error) {.#None} false))))) (in (do async.monad [sent? (async.future (do io.monad [actor (/.spawn! /.default 0) sent? (/.mail! ++! actor)] (in (..mailed? sent?))))] (unit.coverage [/.Behavior /.Mail /.default /.spawn! /.mail!] sent?))) (in (do async.monad [result (async.future (do io.monad [counter (/.spawn! /.default 0) _ (/.poison! counter)] (/.mail! ++! counter)))] (unit.coverage [/.dead] (when result {try.#Success outcome} false {try.#Failure error} (exception.match? /.dead error))))) (let [die! (is (/.Mail Nat) (function (_ state actor) (async#in (exception.except ..got_wrecked []))))] (in (do async.monad [result (async.future (do io.monad [actor (/.spawn! /.default initial_state) sent? (/.mail! die! actor) alive? (/.alive? actor) obituary (/.obituary' actor)] (in {try.#Success [actor sent? alive? obituary]})))] (unit.coverage [/.Obituary /.obituary'] (when result {try.#Success [actor sent? alive? {.#Some [error state (list single_pending_message)]}]} (and (..mailed? sent?) (not alive?) (exception.match? ..got_wrecked error) (n.= initial_state state) (same? die! single_pending_message)) _ false))))) (in (do async.monad [counter (async.future (/.spawn! ..counter 0)) result (do (try.with async.monad) [output_1 (/.tell! (count! 1) counter) output_2 (/.tell! (count! 1) counter) output_3 (/.tell! (count! 1) counter)] (in (and (n.= 1 output_1) (n.= 2 output_2) (n.= 3 output_3))))] (unit.coverage [/.Message /.tell!] (when result {try.#Success outcome} outcome {try.#Failure error} false)))) (do ! [num_events (at ! each (|>> (n.% 10) ++) random.nat) events (random.list num_events random.nat) num_observations (at ! each (n.% num_events) random.nat) .let [expected (list.first num_observations events) sink (is (Atom (Sequence Nat)) (atom.atom sequence.empty))]] (in (do async.monad [agent (async.future (do [! io.monad] [agent (/.spawn! /.default 0) _ (/.observe! (function (_ event stop) (function (_ events_seen self) (async.future (if (n.< num_observations events_seen) (do ! [_ (atom.update! (sequence.suffix event) sink)] (in {try.#Success (++ events_seen)})) (do ! [_ stop] (in {try.#Failure "YOLO"})))))) (frp.sequential 0 events) agent)] (in agent))) _ (/.obituary agent) actual (async.future (atom.read! sink))] (unit.coverage [/.Stop /.observe! /.obituary] (at (list.equivalence n.equivalence) = expected (sequence.list actual)))))) ))))