guix: Use guile-3.0.
[8sync.git] / tests / test-actors.scm
1 ;;; 8sync --- Asynchronous programming for Guile
2 ;;; Copyright (C) 2016 Christopher Allan Webber <cwebber@dustycloud.org>
3 ;;;
4 ;;; This file is part of 8sync.
5 ;;;
6 ;;; 8sync is free software: you can redistribute it and/or modify it
7 ;;; under the terms of the GNU Lesser General Public License as
8 ;;; published by the Free Software Foundation, either version 3 of the
9 ;;; License, or (at your option) any later version.
10 ;;;
11 ;;; 8sync is distributed in the hope that it will be useful,
12 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;;; GNU Lesser General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU Lesser General Public
17 ;;; License along with 8sync.  If not, see <http://www.gnu.org/licenses/>.
18
19 (define-module (tests test-actors)
20   #:use-module (srfi srfi-64)
21   #:use-module (8sync actors)
22   #:use-module (8sync agenda)
23   #:use-module (oop goops)
24   #:use-module (tests utils))
25
26 (test-begin "test-actors")
27
28
29 ;;; Test writing things to here
30 (define %record-out (make-parameter (open-output-string)))
31 (define (~display str)
32   (display str (%record-out)))
33 (define-syntax-rule (~format args ...)
34   (format (%record-out) args ...))
35
36 ;;; Some test dummy values
37 ;;; ======================
38
39 (define %fake-hive-id "the-main-hive")
40 ;; Some fake ids for actors
41 (define %fake-emo-id (make-address "emo" %fake-hive-id))
42 (define %fake-proog-id (make-address "proog" %fake-hive-id))
43 (define %fake-hive-actor-id (make-address "hive" %fake-hive-id))
44
45 (define test-message
46   (make-message ((simple-message-id-generator))
47                 %fake-emo-id
48                 %fake-hive-actor-id ; Bootstrap messages come from the hive
49                 'greet-proog `(#:target ,%fake-proog-id)))
50
51 ;;; Actor utilities
52 ;;; ===============
53
54 ;;; Message tests
55 ;;; =============
56
57 ;; Make sure our test message serializes and deserializes okay
58
59 (let ((reread-message
60        (read-message-from-string
61         (with-output-to-string
62           (lambda () (write-message test-message))))))
63   (test-assert (message? reread-message))
64   ;; Make sure that all the properties are the same from
65   ;; the original message to the re-read message
66   (for-each
67    (lambda (getter)
68      (test-equal (getter test-message) (getter reread-message)))
69    (list message-id message-to message-from message-action message-body
70          message-in-reply-to message-wants-reply
71          (@@ (8sync actors) message-replied))))
72
73
74 ;;; Test reply / autoreply
75 ;;; ======================
76
77 (define-actor <antsy-caller> (<actor>)
78   ((pester-rep (wrap-apply antsy-caller-pester-rep))))
79
80 (define* (antsy-caller-pester-rep actor message #:key who-to-call)
81   (~display "customer> I'm calling customer service about this!\n")
82   (mbody-receive (first-reply #:key msg)
83       (<-wait who-to-call 'field-call)
84     (if (message-auto-reply? first-reply)
85         (~display "customer> Whaaaaat?  I can't believe I got voice mail!\n")
86         (begin
87           (~format "*customer hears*: ~a\n" msg)
88           (mbody-receive (second-reply #:key *auto-reply*)
89               (<-reply-wait first-reply
90                             #:msg "Yes, it didn't work, I'm VERY ANGRY!")
91             (if (message-auto-reply? second-reply)
92                 (~display "customer> Well then!  Harumph.\n")
93                 (error "Not an autoreply?  What's going on here...")))))))
94
95 (define-actor <diligent-rep> (<actor>)
96   ((field-call (wrap-apply rep-field-call))))
97
98 (define (rep-field-call actor message)
99   (~display "good-rep> Hm, another call from a customer...\n")
100   (mbody-receive (reply #:key msg)
101       (<-reply-wait message #:msg "Have you tried turning it off and on?")
102     (~format "*rep hears*: ~a\n" msg)
103     (~display "good-rep> I'm sorry, that's all I can do for you.\n")))
104
105 (define-actor <lazy-rep> (<actor>)
106   ((field-call
107     (lambda (actor message)
108       (~display "lazy-rep> I'm not answering that.\n")))))
109
110 (let* ((hive (make-hive))
111        (customer (bootstrap-actor* hive <antsy-caller> "antsy-caller"))
112        (diligent-rep (bootstrap-actor* hive <diligent-rep> "diligent-rep"))
113        (lazy-rep (bootstrap-actor* hive <lazy-rep> "lazy-rep")))
114   ;; * Playing a tape of a diligent service rep *
115   (parameterize ((%record-out (open-output-string)))
116     (let* ((result (run-hive
117                     hive
118                     (list (bootstrap-message hive customer 'pester-rep
119                                              #:who-to-call diligent-rep))))
120            (displayed-text (get-output-string (%record-out))))
121       (test-equal "customer> I'm calling customer service about this!
122 good-rep> Hm, another call from a customer...
123 *customer hears*: Have you tried turning it off and on?
124 *rep hears*: Yes, it didn't work, I'm VERY ANGRY!
125 good-rep> I'm sorry, that's all I can do for you.
126 customer> Well then!  Harumph.\n"
127         displayed-text)))
128   ;; * Playing a tape of a lazy service rep *
129   (parameterize ((%record-out (open-output-string)))
130     (let* ((result (run-hive
131                     hive
132                     (list (bootstrap-message hive customer 'pester-rep
133                                                   #:who-to-call lazy-rep))))
134            (displayed-text (get-output-string (%record-out))))
135       (test-equal "customer> I'm calling customer service about this!
136 lazy-rep> I'm not answering that.
137 customer> Whaaaaat?  I can't believe I got voice mail!\n"
138           displayed-text))))
139
140 \f
141 ;;; Cleanup tests
142
143 (define-actor <cleanly> (<actor>)
144   ((*cleanup* test-call-cleanup)))
145
146 (define (test-call-cleanup actor message)
147   (speak "Hey, I'm cleanin' up here!\n"))
148
149 (with-fresh-speaker
150  (let ((hive (make-hive)))
151    (bootstrap-actor hive <cleanly>)
152    (run-hive hive '()))
153  (test-equal '("Hey, I'm cleanin' up here!\n")
154    (get-spoken)))
155
156 ;; won't work if we turn off #:cleanup though
157
158 (with-fresh-speaker
159  (let ((hive (make-hive)))
160    (bootstrap-actor hive <cleanly>)
161    (run-hive hive '() #:cleanup #f))
162  (test-equal '()
163    (get-spoken)))
164
165 ;; The exploder self-destructs, even though run-hive has cleanup
166 ;; disabled, because it cleans up on self-destruct.
167
168 (define-actor <exploder> (<actor>)
169   ((explode (lambda (exploder message)
170               (speak "POOF\n")
171               (self-destruct exploder)))
172    (*cleanup* (lambda _ (speak "Cleaning up post-explosion\n")))))
173
174 (with-fresh-speaker
175  (let ((hive (make-hive)))
176    (define exploder (bootstrap-actor hive <exploder>))
177    (run-hive hive (list (bootstrap-message hive exploder 'explode))
178              #:cleanup #f))
179  (test-equal '("POOF\n" "Cleaning up post-explosion\n")
180    (get-spoken)))
181
182 (define-class <hi-on-init> (<actor>)
183   (name #:init-keyword #:name)
184   (create-friend #:init-value #f
185                  #:init-keyword #:create-friend)
186   (actions #:allocation #:each-subclass
187            #:init-thunk (build-actions
188                          (*init* hi-on-init-init))))
189
190 (define (hi-on-init-init actor message)
191   (speak (format #f "Hi! ~a inits now.\n"
192                  (slot-ref actor 'name)))
193   (and=> (slot-ref actor 'create-friend)
194          (lambda (friend-name)
195            (create-actor actor <hi-on-init> #:name friend-name))))
196
197 (with-fresh-speaker
198  (let ((hive (make-hive)))
199    (define hi-on-init (bootstrap-actor hive <hi-on-init>
200                                        #:name "jack"
201                                        #:create-friend "jill"))
202    (run-hive hive '()))
203  (test-equal (get-spoken)
204    '("Hi! jack inits now.\n" "Hi! jill inits now.\n")))
205
206 (test-end "test-actors")
207 (test-exit)