diff --git a/racket/syndicate/info.rkt b/racket/syndicate/info.rkt index a2db563..ec4e9a2 100644 --- a/racket/syndicate/info.rkt +++ b/racket/syndicate/info.rkt @@ -2,3 +2,4 @@ (define scribblings '(("scribblings/syndicate.scrbl" ()))) (define racket-launcher-names '("syndicate-broker")) (define racket-launcher-libraries '("broker/server.rkt")) +(define test-include-paths '("syndicate/tests")) diff --git a/racket/syndicate/little-actors/core.rkt b/racket/syndicate/little-actors/core.rkt index 5e18a58..ee2770e 100644 --- a/racket/syndicate/little-actors/core.rkt +++ b/racket/syndicate/little-actors/core.rkt @@ -1,5 +1,9 @@ #lang racket +(provide run + run-with + run-with-trace) + (require syndicate/monolithic) (require syndicate/trie) (require racket/set) @@ -356,7 +360,7 @@ (lambda (rev-vs σ e) (result-map (lambda (v) (cons v rev-vs)) (eval-exp e Γ σ))))) - (result-map (lambda (rev-vs) (cons 'list (reverse rev-vs))) + (result-map (lambda (rev-vs) (reverse rev-vs)) res)] [`(let (,x ,exp) ,body-exp) (result-bind (eval-exp exp Γ σ) @@ -575,9 +579,8 @@ (define (eval-pat pat Γ σ) (match pat [`(list ,pats ...) - (cons 'list - (for/list ([p (in-list pats)]) - (eval-pat p Γ σ)))] + (for/list ([p (in-list pats)]) + (eval-pat p Γ σ))] [`(observe ,pat) (observe (eval-pat pat Γ σ))] [`(inbound ,pat) @@ -649,14 +652,14 @@ (check-equal? (occurrences `(asserted 5) (message 5) trie-empty mt-Γ mt-σ) (list)) (check-equal? (occurrences `(asserted (list "price" $x)) - (scn (assertion '(list "price" 12))) - (assertion '(list "price" 5)) + (scn (assertion '("price" 12))) + (assertion '("price" 5)) mt-Γ mt-σ) (list (list (binding 'x 12)))) (check-equal? (list->set (occurrences `(asserted (list "price" $x)) - (scn (π-union (assertion '(list "price" 12)) (assertion '(list "price" 16)))) - (assertion '(list "price" 5)) + (scn (π-union (assertion '("price" 12)) (assertion '("price" 16)))) + (assertion '("price" 5)) mt-Γ mt-σ)) (set (list (binding 'x 12)) (list (binding 'x 16))))) @@ -743,6 +746,14 @@ (boot-actor boot mt-Γ))) (run-ground (cons upside-down-relay boot-actions))) +;; Actor Program -> Syndicate +(define (run-with regular-actor p) + (define boot-actions + (for/list ([boot (in-list p)]) + (boot-actor boot mt-Γ))) + (run-ground regular-actor boot-actions)) + + ;; Actor AsyncChannel Program -> Boolean ;; trace-actor is the first actor spawned inside the program's ground dataspace ;; chan is a channel used by the trace-actor to signal a completed trace, by @@ -799,92 +810,6 @@ ;; Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(module+ test - - (define test-program - `((spawn (on-start (printf "hello,world\n"))))) - - (define test-program2 - `( - (spawn (on (asserted 5) - (printf "wat\n"))) - (spawn (assert 5)))) - - (test-trace (trace (assertion-added (observe 5)) - (assertion-added 5)) - test-program2) - - - (define ping-pong - `( - (spawn (on (message "ping") - (printf "ping\n") - (send! "pong"))) - (spawn (on (message "pong") - (printf "pong\n") - (send! "ping")) - (on-start (send! "ping"))))) - - (test-trace (trace (message "ping") - (message "pong") - (message "ping") - (message "pong") - (message "ping") - (message "pong") - (message "ping") - (message "pong")) - ping-pong) - - (define bank-account - `( - (spawn (field [balance 0]) - (assert (list "account" (balance))) - (on (message (list "deposit" $amount)) - (balance (+ (balance) amount)))) - - (spawn (on (asserted (list "account" $balance)) - (printf "Balance changed to ~a\n" balance)) - (stop-when (asserted (list "account" 70)) - (printf "bye\n")) - (on-stop (printf "good.\n"))) - - (spawn (stop-when (asserted (observe (list "deposit" _))) - (send! (list "deposit" +100)) - (send! (list "deposit" -30)))))) - - (test-trace (trace (assertion-added '(list "account" 0)) - (and (assertion-added '(list "account" 100)) - (assertion-removed '(list "account" 0))) - (and (assertion-added '(list "account" 70)) - (assertion-removed '(list "account" 100)))) - bank-account - #:timeout 5000) - - (define multi-level-ex - '( - (spawn (on (asserted "hello") - (printf "goodbye"))) - (dataspace (spawn (assert (outbound "hello")))))) - - (test-trace (trace (assertion-added "hello")) - multi-level-ex) - - (define multi-level-message - '( - (spawn (on (message "hello"))) - (dataspace (spawn (on-start (send! (outbound "hello"))))))) - - (test-trace (trace (message "hello")) - multi-level-message) - (define multi-level-other-way - '( - (dataspace (spawn (on (asserted (inbound "gday")) - (send! (outbound "good things"))))) - (spawn (assert "gday")))) - - (test-trace (trace (message "good things")) - multi-level-other-way)) - (define ff '( (spawn (on (message 5) @@ -942,41 +867,6 @@ (on (message (list "x" $x)) (printf "x = ~v\n" x))))) -(module+ test - ;; test that terminating facets can create new facets (at the parent level) - (define stop-when-react - '( - (spawn (stop-when (message "stop") - (react (on (message "poodle") - (send! "success") - (printf "woohoo\n"))))) - (spawn (on-start (send! "stop")) - (on (asserted (observe "poodle")) - (send! "poodle"))))) - (test-trace (trace (message "success")) - stop-when-react) - ;; Reflects the current behavior, but quite possibly *not* what should happen - (define create-new-facet-inside-on-stop - '( - (spawn - (on-stop (react (assert (outbound "here")))) - (stop-when (message "stop"))) - - (spawn (on-start (send! "stop"))))) - (test-trace (trace (assertion-added (outbound "here"))) - create-new-facet-inside-on-stop) - ;; Similarly dubious; create new facets from more nested facets - (define facet-creation-during-stop-from-grandchild - '( - (spawn (on-start - (react (on-stop - (react (assert (outbound "inner")))))) - (stop-when (message "stop") - (react (assert (outbound "outer"))))) - - (spawn (on-start (send! "stop"))))) - (test-trace (trace (assertion-added (outbound "inner"))) - facet-creation-during-stop-from-grandchild)) (module+ test (define do-new-facets-run-immediately @@ -988,31 +878,6 @@ (check-false (run-with-trace (trace (message "I am here")) do-new-facets-run-immediately))) -(module+ test - (define use-current-knowledge-with-new-facet - '( - (spawn (on (asserted "hello") - (react (on (asserted "hello") - (printf "do I run?\n") - (send! "yes indeed"))))) - - (spawn (assert "hello")))) - (test-trace (trace (message "yes indeed")) - use-current-knowledge-with-new-facet) - - (define maintain-knowledge-across-events - '( - (spawn (on (asserted "outer") - (react (on (message "bam") - (react (on (asserted "outer") - (send! "icu") - (printf "icu\n"))))))) - (spawn (assert "outer") - (on (asserted (observe "bam")) - (send! "bam"))))) - (test-trace (trace (message "icu")) - maintain-knowledge-across-events)) - (module+ test ;; this should bring down the actor *but not* the entire program (define escaping-field @@ -1024,14 +889,3 @@ (check-false (run-with-trace (trace (message "success!")) escaping-field)) (check-not-exn (lambda () (run escaping-field)))) - -(module+ test - ;; starting exceptions - (define nested-spawn-exceptions - '( - (spawn (on (message "go") - (spawn (on-start (/ 1 0))) - (send! "lovely happiness"))) - (spawn (on-start (send! "go"))))) - (test-trace (trace (message "lovely happiness")) - nested-spawn-exceptions)) diff --git a/racket/syndicate/monitor.rkt b/racket/syndicate/monitor.rkt index b72d0db..a6adf48 100644 --- a/racket/syndicate/monitor.rkt +++ b/racket/syndicate/monitor.rkt @@ -12,7 +12,8 @@ (provide trace-actor spawn-monitor assertion-added - assertion-removed) + assertion-removed + (for-syntax trace)) (module+ test (require rackunit)) diff --git a/racket/syndicate/test-lang.rkt b/racket/syndicate/test-lang.rkt new file mode 100644 index 0000000..7e345f7 --- /dev/null +++ b/racket/syndicate/test-lang.rkt @@ -0,0 +1,8 @@ +#lang racket/base + +(require "test.rkt") +(require "actor-lang.rkt") + +(provide (except-out (all-from-out "test.rkt") test-module-begin) + (except-out (all-from-out "actor-lang.rkt") #%module-begin) + (rename-out [test-module-begin #%module-begin])) \ No newline at end of file diff --git a/racket/syndicate/test.rkt b/racket/syndicate/test.rkt new file mode 100644 index 0000000..c6aa9e4 --- /dev/null +++ b/racket/syndicate/test.rkt @@ -0,0 +1,74 @@ +#lang racket + +(provide test-module-begin + (rename-out [illegal-trace trace])) + +(module reader syntax/module-reader + syndicate/test-lang) + +(require racket/async-channel) +(require racket/engine) + +(require (prefix-in big: "actor-lang.rkt")) +(require "monitor.rkt") +(require "upside-down.rkt") +(require (prefix-in little: (only-in "little-actors/core.rkt" run-with))) +(require (for-syntax syntax/parse)) +(require rackunit) + +(define-syntax (illegal-trace stx) + (raise-syntax-error #f "trace: only allowed at top level of a test module" stx)) + +(begin-for-syntax + (define (trace->actor+channel trace-stx) + #`(let ([chan (make-async-channel)]) + (cons (trace-actor #,trace-stx + (lambda () (async-channel-put chan #t))) + chan)))) + +(define-syntax (test-module-begin stx) + (define-syntax-class not-trace + #:datum-literals (trace) + (pattern (~and _:expr (~not _:trace)))) + (syntax-parse stx + ;; only allow one trace! + [(_ (~or (~once t:trace) + exps:expr) + ...) + #'(#%module-begin + ;; do I need to worry about catching exceptions here? + (define big-result (run-with-trace big:run-ground t (list (spawn-upside-down exps) ...))) + (define little-result (run-with-trace little:run-with t '(exps ...))) + (check-equal? big-result little-result) + ;; it would be nice to specify false-y traces, too + (check-true big-result) + (check-true little-result))])) + +(define DEFAULT-FUEL 2000) + +(define-syntax-rule (run-with-trace run-ground trc act-exps) + (let ([chan (make-async-channel)]) + (test-harness run-ground + chan + (trace-actor trc (lambda () (async-channel-put chan #t))) + act-exps + DEFAULT-FUEL))) + +(define (test-harness run-ground chan trace-act acts [timeout never-evt]) + (define syndicate-thread + (thread (lambda () + (engine-run timeout + (engine (lambda (x) (run-ground trace-act acts))))))) + (define result + (sync (handle-evt chan + (lambda (val) #t)) + (handle-evt syndicate-thread + (lambda (val) + ;; it's possible one of the final events in the + ;; dataspace resulted in an accepting trace and the + ;; thread ended at the same time, so the scheduler + ;; picked this event. Double check the channel for this + ;; case. + (async-channel-try-get chan))))) + (kill-thread syndicate-thread) + result) diff --git a/racket/syndicate/tests/bank-account.rkt b/racket/syndicate/tests/bank-account.rkt new file mode 100644 index 0000000..d73c99d --- /dev/null +++ b/racket/syndicate/tests/bank-account.rkt @@ -0,0 +1,22 @@ +#lang syndicate/test + +(spawn (field [balance 0]) + (assert (list "account" (balance))) + (on (message (list "deposit" $amount)) + (balance (+ (balance) amount)))) + +(spawn (on (asserted (list "account" $balance)) + (printf "Balance changed to ~a\n" balance)) + (stop-when (asserted (list "account" 70)) + (printf "bye\n")) + (on-stop (printf "good.\n"))) + +(spawn (stop-when (asserted (observe (list "deposit" _))) + (send! (list "deposit" +100)) + (send! (list "deposit" -30)))) + +(trace (assertion-added '("account" 0)) + (and (assertion-added '("account" 100)) + (assertion-removed '("account" 0))) + (and (assertion-added '("account" 70)) + (assertion-removed '("account" 100)))) \ No newline at end of file diff --git a/racket/syndicate/tests/boop.rkt b/racket/syndicate/tests/boop.rkt new file mode 100644 index 0000000..ce2d4ae --- /dev/null +++ b/racket/syndicate/tests/boop.rkt @@ -0,0 +1,13 @@ +#lang syndicate/actor + +(dataspace + + (spawn (on (message "hello") + (printf "got hello\n"))) + + (spawn (assert "I am here") + (on-start (printf "hello\n") + (send! "hello")))) + +#;(dataspace + (send! 5)) \ No newline at end of file diff --git a/racket/syndicate/tests/create-new-facet-in-on-stop.rkt b/racket/syndicate/tests/create-new-facet-in-on-stop.rkt new file mode 100644 index 0000000..113906e --- /dev/null +++ b/racket/syndicate/tests/create-new-facet-in-on-stop.rkt @@ -0,0 +1,12 @@ +#lang syndicate/test + +;; Reflects the current behavior of the little implementation, +;; but quite possibly *not* what should happen + +(spawn + (on-stop (react (assert (outbound "here")))) + (stop-when (message "stop"))) + +(spawn (on-start (send! "stop"))) + +(trace (assertion-added (outbound "here"))) \ No newline at end of file diff --git a/racket/syndicate/tests/facet-creation-during-stop-from-grandchild.rkt b/racket/syndicate/tests/facet-creation-during-stop-from-grandchild.rkt new file mode 100644 index 0000000..a73c323 --- /dev/null +++ b/racket/syndicate/tests/facet-creation-during-stop-from-grandchild.rkt @@ -0,0 +1,16 @@ +#lang syndicate/test + +;; currently FAILS + +;; dubious behavior by little implementation; +;; create new facets from more nested facets + +(spawn (on-start + (react (on-stop + (react (assert (outbound "inner")))))) + (stop-when (message "stop") + (react (assert (outbound "outer"))))) + +(spawn (on-start (send! "stop"))) + +(trace (assertion-added (outbound "inner"))) diff --git a/racket/syndicate/tests/maintain-knowledge-across-events.rkt b/racket/syndicate/tests/maintain-knowledge-across-events.rkt new file mode 100644 index 0000000..6af348d --- /dev/null +++ b/racket/syndicate/tests/maintain-knowledge-across-events.rkt @@ -0,0 +1,13 @@ +#lang syndicate/test + +(spawn (on (asserted "outer") + (react (on (message "bam") + (react (on (asserted "outer") + (send! "icu") + (printf "icu\n"))))))) + +(spawn (assert "outer") + (on (asserted (observe "bam")) + (send! "bam"))) + +(trace (message "icu")) \ No newline at end of file diff --git a/racket/syndicate/tests/multi-level-message.rkt b/racket/syndicate/tests/multi-level-message.rkt new file mode 100644 index 0000000..22969b9 --- /dev/null +++ b/racket/syndicate/tests/multi-level-message.rkt @@ -0,0 +1,14 @@ +#lang syndicate/test + +;; currently failing, seemingly due to a bug in the big implementation; see +;; https://github.com/tonyg/syndicate/issues/20 + +(dataspace + (spawn (on (message "hello") + (printf "got hello\n"))) + + (spawn (assert 12) + (on-start (printf "hello\n") + (send! (outbound "hello"))))) + +(trace (message "hello")) \ No newline at end of file diff --git a/racket/syndicate/tests/multi-level-other-way.rkt b/racket/syndicate/tests/multi-level-other-way.rkt new file mode 100644 index 0000000..b03d77c --- /dev/null +++ b/racket/syndicate/tests/multi-level-other-way.rkt @@ -0,0 +1,8 @@ +#lang syndicate/test + +(dataspace (spawn (on (asserted (inbound "gday")) + (send! (outbound "good things"))))) + +(spawn (assert "gday")) + +(trace (message "good things")) \ No newline at end of file diff --git a/racket/syndicate/tests/nested-spawn-exceptions.rkt b/racket/syndicate/tests/nested-spawn-exceptions.rkt new file mode 100644 index 0000000..d5bbbcb --- /dev/null +++ b/racket/syndicate/tests/nested-spawn-exceptions.rkt @@ -0,0 +1,9 @@ +#lang syndicate/test + +(spawn (on (message "go") + (spawn (on-start (/ 1 0))) + (send! "lovely happiness"))) + +(spawn (on-start (send! "go"))) + +(trace (message "lovely happiness")) \ No newline at end of file diff --git a/racket/syndicate/tests/ping-pong.rkt b/racket/syndicate/tests/ping-pong.rkt new file mode 100644 index 0000000..a867427 --- /dev/null +++ b/racket/syndicate/tests/ping-pong.rkt @@ -0,0 +1,19 @@ +#lang syndicate/test + +(spawn (on (message "ping") + (printf "ping\n") + (send! "pong"))) + +(spawn (on (message "pong") + (printf "pong\n") + (send! "ping")) + (on-start (send! "ping"))) + +(trace (message "ping") + (message "pong") + (message "ping") + (message "pong") + (message "ping") + (message "pong") + (message "ping") + (message "pong")) \ No newline at end of file diff --git a/racket/syndicate/tests/simple-multi-level.rkt b/racket/syndicate/tests/simple-multi-level.rkt new file mode 100644 index 0000000..4855457 --- /dev/null +++ b/racket/syndicate/tests/simple-multi-level.rkt @@ -0,0 +1,8 @@ +#lang syndicate/test + +(spawn (on (asserted "hello") + (printf "goodbye\n"))) + +(dataspace (spawn (assert (outbound "hello")))) + +(trace (assertion-added "hello")) \ No newline at end of file diff --git a/racket/syndicate/tests/simple1.rkt b/racket/syndicate/tests/simple1.rkt new file mode 100644 index 0000000..be9eb4c --- /dev/null +++ b/racket/syndicate/tests/simple1.rkt @@ -0,0 +1,9 @@ +#lang syndicate/test + +(spawn (on (asserted 5) + (printf "wat\n"))) + +(spawn (assert 5)) + +(trace (assertion-added (observe 5)) + (assertion-added 5)) \ No newline at end of file diff --git a/racket/syndicate/tests/stop-when-react.rkt b/racket/syndicate/tests/stop-when-react.rkt new file mode 100644 index 0000000..779d3e1 --- /dev/null +++ b/racket/syndicate/tests/stop-when-react.rkt @@ -0,0 +1,14 @@ +#lang syndicate/test + +;; test that terminating facets can create new facets (at the parent level) + +(spawn (stop-when (message "stop") + (react (on (message "poodle") + (send! "success") + (printf "woohoo\n"))))) + +(spawn (on-start (send! "stop")) + (on (asserted (observe "poodle")) + (send! "poodle"))) + +(trace (message "success")) diff --git a/racket/syndicate/tests/use-current-knowledge-with-new-facet.rkt b/racket/syndicate/tests/use-current-knowledge-with-new-facet.rkt new file mode 100644 index 0000000..84791d0 --- /dev/null +++ b/racket/syndicate/tests/use-current-knowledge-with-new-facet.rkt @@ -0,0 +1,10 @@ +#lang syndicate/test + +(spawn (on (asserted "hello") + (react (on (asserted "hello") + (printf "do I run?\n") + (send! "yes indeed"))))) + +(spawn (assert "hello")) + +(trace (message "yes indeed")) \ No newline at end of file