(require (file "../prometheus.ss")
         (file "assert-module.scm"))
;;; This is a simple testsuite.
;;; It requires my ASSERT macros

;;; ,open assert prometheus
;;; And run the following:

(define (prometheus-test)
  (define (println . args)
    (for-each display args)

  (println "Testing Prometheus")
  (println "==================")
  (println "CLONE")
  (println "-----")
  (let* ((o1 (*the-root-object* 'clone))
         (o2 (o1 'clone)))
    (assert "Parent slot set by clone"
      (eq? *the-root-object* (o1 'parent)))

    (println "ADD-VALUE-SLOT!")
    (println "---------------")
    (o1 'add-value-slot! 'fnord 17)
    (assert "Read-Only Getter"
      (= 17 (o1 'fnord)))
    (o1 'add-value-slot! 'fnord 'set-fnord! 23)
    (assert "Getter"
      (= 23 (o1 'fnord)))
    (o2 'set-fnord! 5)
    (assert "Setter"
      (= 5 (o2 'fnord)))
    (assert "Setter does not modify parent"
      (= 23 (o1 'fnord)))

    (println "ADD-METHOD-SLOT!")
    (println "----------------")
    (o1 'add-method-slot! 'add (lambda (self resend a b)
                                 (+ a b)))
    (assert "Read-only getter"
      (= 5 (o1 'add 2 3)))
    (o1 'add-method-slot! 'add 'set-add! (lambda (self resend a b)
                                           (+ a b)))
    (assert "Getter"
      (= 17 (o2 'add 10 7)))
    (o2 'set-add! (lambda (self resend a b)
                    (* a b)))
    (assert "Setter"
      (= 42 (o2 'add 6 7)))
    (assert "Setter does not modify parent"
      (= 17 (o1 'add 10 7)))

  (println "ADD-PARENT-SLOT!")
  (println "----------------")
  (let* ((testparent (*the-root-object* 'clone))
         (o1 (*the-root-object* 'clone))
         (o2 (o1 'clone))
         (otherparent (*the-root-object* 'clone)))
    (testparent 'add-value-slot! 'testparent #t)
    (o1 'add-parent-slot! 'parent2 testparent)
    (assert "Children inherit stuff from their parents"
      (eq? #t (o2 'testparent)))
    (o1 'add-parent-slot! 'parent2 'set-parent2 testparent)
    (assert "Parent with setter slot"
      (eq? #t (o2 'testparent)))
    (otherparent 'add-value-slot! 'otherparent #f)
    (o2 'set-parent2 otherparent)
    (assert "Setter modifies parent slot"
      (eq? #f (o2 'otherparent)))
    (assert "Setter does not modify parents"
      (eq? #t (o1 'testparent)))
    (o2 'add-value-slot! 'parent2 #f)
    (assert-fails "Other types of slot overwrite parent slots"
      (o2 'otherparent))

  (println "SELF")
  (println "----")
  (let* ((o1 (*the-root-object* 'clone))
         (o2 (o1 'clone)))
    (o1 'add-method-slot! 'get-self (lambda (self resend)
    (assert "Self is passed correctly in inheritance"
      (eq? o2 (o2 'get-self)))

  (println "Resends")
  (println "-------")
  (let* ((a (*the-root-object* 'clone))
         (b (a 'clone))
         (c (b 'clone)))
    (define-method (a 'info self resend)
    (define-method (b 'info self resend)
    (define-method (c 'info self resend)
    (define-method (c 'get-info self resend where)
      (resend where 'info))
    (c 'add-value-slot! 'parent2 a)

    (assert "Local resend"
      (eq? 'c (c 'get-info #t)))
    (assert "Undirected resend"
      (eq? 'b (c 'get-info #f)))
    (assert "Directed resend"
      (eq? 'b (c 'get-info 'parent)))
    (assert "Directed resend to a non-parent"
      (eq? 'a (c 'get-info 'parent2)))

  (println "Error handling")
  (println "--------------")

  (let* ((o1 (*the-root-object* 'clone))
         (o2.1 (o1 'clone))
         (o2.2 (o1 'clone))
         (o3 (o2.1 'clone)))
    (o3 'add-parent-slot! 'parent2 o2.2)
    (o2.1 'add-value-slot! 'fnord 5)
    (o2.2 'add-value-slot! 'fnord 23)
    (assert-fails "Unknown message signals error"
      (o3 'gobble-gobble-gobble-gobble-gobble))
    (assert-fails "Ambiguous message signals error"
      (o3 'fnord))

    (o1 'add-method-slot! 'message-not-understood
        (lambda (self resend msg args)
          (cons 'message-not-understood
                (cons msg args))))
    (o1 'add-method-slot! 'ambiguous-message-send
        (lambda (self resend msg args)
          (cons 'ambiguous-message-send
                (cons msg args))))

    (assert "Message-not-understood is called correctly"
      (equal? '(message-not-understood foo 1 2 3) (o3 'foo 1 2 3)))
    (assert "Ambiguous-message-send is called correctly"
      (equal? '(ambiguous-message-send fnord 5 17 23) (o3 'fnord 5 17 23)))

    ;; FIXME! This might/should already die?
    ;; (o3 'add-parent-slot! 'parent3 #f)
    ;; We can write an PROMETHEUS-OBJECT? predicate
    ;; with the help of an error handler...
    (o3 'add-parent-slot! 'parent3 (lambda args #f))
    (assert-fails "Parent slots that are not objects cause an error"
      (o3 'really-does-not-exist))

  (let* ((a (*the-root-object* 'clone))
         (b (a 'clone)))
    (a 'add-parent-slot! 'parent2 b)
    (assert-fails "Parent cycles don't cause infinite loops"
      (a 'skiddoo))