aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/test/lux/control/concurrency/actor.lux
blob: 017ee60eff4dae24871efddbefc1e3848525cfe4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
(.module:
  [lux #*
   ["_" test (#+ Test)]
   [abstract/monad (#+ do)]
   [control
    ["ex" exception]
    ["." io (#+ IO io)]]
   [data
    ["." error]
    [text
     ["%" format (#+ format)]]]
   [math
    ["r" random]]]
  {1
   ["." / (#+ actor: message:)
    [//
     ["." promise ("#;." monad)]]]})

(actor: Counter
  Nat

  ((handle message state self)
   (do (error.with promise.monad)
     [#let [_ (log! "BEFORE")]
      output (message state self)
      #let [_ (log! "AFTER")]]
     (wrap output)))

  ((stop cause state)
   (promise;wrap (log! (if (ex.match? /.poisoned cause)
                         (format "Counter was poisoned: " (%.nat state))
                         cause)))))

(message: #export Counter
  (count! {increment Nat} state self Nat)
  (let [state' (n/+ increment state)]
    (promise;wrap (#error.Success [state' state']))))

(def: #export test
  Test
  (do r.monad
    [_ (wrap [])]
    (<| (_.context (%.name (name-of /.Actor)))
        ($_ _.and
            (_.test "Can check if an actor is alive."
                    (io.run (do io.monad
                              [counter (new@Counter 0)]
                              (wrap (/.alive? counter)))))

            (_.test "Can poison actors."
                    (io.run (do io.monad
                              [counter (new@Counter 0)
                               poisoned? (/.poison counter)]
                              (wrap (and poisoned?
                                         (not (/.alive? counter)))))))
            
            (_.test "Cannot poison an already dead actor."
                    (io.run (do io.monad
                              [counter (new@Counter 0)
                               first-time (/.poison counter)
                               second-time (/.poison counter)]
                              (wrap (and first-time
                                         (not second-time))))))

            (:: r.monad wrap
                (do promise.monad
                  [result (do (error.with promise.monad)
                            [#let [counter (io.run (new@Counter 0))]
                             output-1 (count! 1 counter)
                             output-2 (count! 1 counter)
                             output-3 (count! 1 counter)]
                            (wrap (and (n/= 1 output-1)
                                       (n/= 2 output-2)
                                       (n/= 3 output-3))))]
                  (_.assert "Can send messages to actors."
                            (case result
                              (#error.Success outcome)
                              outcome

                              (#error.Failure error)
                              #0))))
            ))))