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