Wolf sheep works in tests
[clnl] / src / test / main.lisp
1 (in-package #:clnl-test)
2
3 (defparameter *tests* nil)
4
5 (defun run-and-print-test (test)
6  (let
7   ((green (format nil "~c[1;32m" #\Esc))
8    (red (format nil "~c[1;31m" #\Esc))
9    (result (funcall (cadr test))))
10   (format t "~A- ~S ~A~c[0m~%" (if result green red) (car test) (if result "passed" "failed") #\Esc)
11   result))
12
13 (defun run-tests (tests)
14  (let
15   ((final-result t))
16   (loop
17    :for test :in tests
18    :for result := (run-and-print-test test)
19    :do (setf final-result (and final-result result)))
20   final-result))
21
22 (defun run-all-tests ()
23  (run-tests (reverse *tests*)))
24
25 (defun run-tests-matching (match)
26  (run-tests
27   (remove-if-not (lambda (test-name) (cl-ppcre:scan (format nil "^~A$" match) test-name)) *tests* :key #'car)))
28
29 (defun find-test (name)
30  (or
31   (find name *tests* :test #'string= :key #'car)
32   (error "Couldn't find test with name: ~A" name)))
33
34 (defun test-debug (name) (format t "----~%~A~%" (funcall (third (find-test name)))))
35 (defun test-scala-prog (name) (format t "----~%~A~%" (fourth (find-test name))))
36 (defun test-scala-input (name) (format t "----~%~A~%" (fifth (find-test name))))
37
38 (defmacro defsimpletest (name test-fn debug-fn scala-prog scala-input)
39  `(progn
40    ;(when (find-test ,name) (error "Test with name ~S already exists, abort, abort" ,name))
41    (push
42     (list ,name ,test-fn ,debug-fn ,scala-prog ,scala-input)
43     *tests*)))
44
45 (defun checksum= (expected got)
46  (if (stringp expected)
47   (string= got expected)
48   (find got expected :test #'string=)))
49
50 ; To be used only with the simplest of tests, just a list of commands and a checksum of the
51 ; world after they've been run.
52 (defmacro defsimplecommandtest (name commands checksum)
53  `(defsimpletest
54    (format nil "Simple Command - ~A" ,name)
55    (lambda ()
56     (clnl:boot "resources/empty.nlogo" t)
57     (clnl:run-commands ,commands)
58     (checksum= ,checksum (checksum-world)))
59    (lambda ()
60     (clnl:boot "resources/empty.nlogo" t)
61     (clnl:run-commands ,commands)
62     (format nil "~A~A"
63      (clnl-nvm:export-world)
64      (checksum-world)))
65    "bin/runcmd.scala"
66    (format nil "~A~%" ,commands)))
67
68 (defmacro defsimplereportertest (name reporter value checksum)
69  `(defsimpletest
70    (format nil "Simple Reporter - ~A" ,name)
71    (lambda ()
72     (clnl:boot "resources/empty.nlogo" t)
73     (and
74      (string= (funcall (intern "DUMP-OBJECT" :clnl-nvm) (clnl:run-reporter ,reporter)) ,value)
75      (checksum= ,checksum (checksum-world))))
76    (lambda ()
77     (clnl:boot "resources/empty.nlogo" t)
78     (format nil "~A~%~A~A"
79      (funcall (intern "DUMP-OBJECT" :clnl-nvm) (clnl:run-reporter ,reporter))
80      (clnl-nvm:export-world)
81      (checksum-world)))
82    "bin/runcmd.scala"
83    (format nil "@#$#@#$#@~A" ,reporter)))
84
85 (defmacro defreportertestwithsetup (name setup reporter value checksum)
86  `(defsimpletest
87    (format nil "Reporter With Setup - ~A" ,name)
88    (lambda ()
89     (clnl:boot "resources/empty.nlogo" t)
90     (clnl:run-commands ,setup)
91     (and
92      (string= (funcall (intern "DUMP-OBJECT" :clnl-nvm) (clnl:run-reporter ,reporter)) ,value)
93      (checksum= ,checksum (checksum-world))))
94    (lambda ()
95     (clnl:boot "resources/empty.nlogo" t)
96     (clnl:run-commands ,setup)
97     (format nil "~A~%~A~A"
98      (funcall (intern "DUMP-OBJECT" :clnl-nvm) (clnl:run-reporter ,reporter))
99      (clnl-nvm:export-world)
100      (checksum-world)))
101    "bin/runcmd.scala"
102    (format nil "~A@#$#@#$#@~A" ,setup ,reporter)))
103
104 (defun model-code->nlogo (code)
105  (format nil
106   "~A
107 @#$#@#$#@
108 GRAPHICS-WINDOW~%210~%10~%649~%470~%-1~%-1~%13.0~%1~%10~%1~%1~%1~%0~%1~%1~%1~%-1~%1~%-1~%1~%0~%0~%1~%ticks~%30.0~%
109 @#$#@#$#@
110 "
111   code))
112
113 (defmacro defmodeltest (name model commands reporter value checksum)
114  `(defsimpletest
115    ,name
116    (lambda ()
117     (let
118      ((model (with-input-from-string (str ,(model-code->nlogo model)) (clnl-model:read-from-nlogo str))))
119      (and
120       (let
121        ((callback nil))
122        (declaim (sb-ext:muffle-conditions cl:warning))
123        (eval (clnl:model->single-form-lisp model :netlogo-callback (lambda (f) (setf callback f))))
124        (when ,commands (funcall callback ,commands))
125        (and
126         (or (not ,reporter) (string= (funcall (intern "DUMP-OBJECT" :clnl-nvm) (funcall callback ,reporter)) ,value))
127         (checksum= ,checksum (checksum-world))))
128       (let*
129        ((pkg (make-package (gensym)))
130         (clnl:*model-package* pkg)
131         (prev-package *package*))
132        (eval
133         (cons
134          'progn
135          (clnl:model->multi-form-lisp model (intern "BOOT-ME" pkg)
136           :netlogo-callback-fn (intern "NETLOGO-CALLBACK" pkg))))
137        (eval `(in-package ,(package-name prev-package)))
138        (funcall (symbol-function (intern "BOOT-ME" pkg)))
139        (when ,commands (funcall (symbol-function (intern "NETLOGO-CALLBACK" pkg)) ,commands))
140        (and
141         (or
142          (not ,reporter)
143          (string=
144           (funcall (intern "DUMP-OBJECT" :clnl-nvm) (funcall (intern "NETLOGO-CALLBACK" pkg) ,reporter))
145           ,value))
146         (checksum= ,checksum (checksum-world)))))))
147    (lambda ()
148     (let
149      ((callback nil))
150      (declaim (sb-ext:muffle-conditions cl:warning))
151      (eval
152       (clnl:model->single-form-lisp
153        (with-input-from-string (str ,(model-code->nlogo model)) (clnl-model:read-from-nlogo str))
154        :netlogo-callback (lambda (f) (setf callback f))))
155      (when ,commands (funcall callback ,commands))
156      (format nil "~A~A~A"
157       (if ,reporter (format nil "~A~%" (funcall (intern "DUMP-OBJECT" :clnl-nvm) (funcall callback ,reporter))) "")
158       (clnl-nvm:export-world)
159       (checksum-world))))
160    "bin/runcmd.scala"
161    (format nil "~A@#$#@#$#@~A@#$#@#$#@~A" ,commands (or ,reporter "") ,model)))
162
163 (defmacro defmodelcommandtest (name model commands checksum)
164  `(defmodeltest (format nil "Model Command - ~A" ,name) ,model ,commands nil nil ,checksum))
165
166 (defmacro defmodelreportertest (name model commands reporter value checksum)
167  `(defmodeltest (format nil "Model Reporter - ~A" ,name) ,model ,commands ,reporter ,value ,checksum))
168
169 (defmacro defmodelfiletest (name file commands checksum)
170  `(defsimpletest
171    ,(format nil "File Model - ~A" name)
172    (lambda ()
173     (let
174      ((model (with-open-file (str ,file) (clnl-model:read-from-nlogo str))))
175      (and
176       (let
177        ((callback nil))
178        (declaim (sb-ext:muffle-conditions cl:warning))
179        (eval (clnl:model->single-form-lisp model :netlogo-callback (lambda (f) (setf callback f))))
180        (when ,commands (funcall callback ,commands))
181        (checksum= ,checksum (checksum-world)))
182       (let*
183        ((pkg (make-package (gensym)))
184         (clnl:*model-package* pkg)
185         (prev-package *package*))
186        (eval
187         (cons
188          'progn
189          (clnl:model->multi-form-lisp model (intern "BOOT-ME" pkg)
190           :netlogo-callback-fn (intern "NETLOGO-CALLBACK" pkg))))
191        (eval `(in-package ,(package-name prev-package)))
192        (funcall (symbol-function (intern "BOOT-ME" pkg)))
193        (when ,commands (funcall (symbol-function (intern "NETLOGO-CALLBACK" pkg)) ,commands))
194        (checksum= ,checksum (checksum-world))))))
195    (lambda ()
196     (let
197      ((callback nil))
198      (declaim (sb-ext:muffle-conditions cl:warning))
199      (eval
200       (clnl:model->single-form-lisp
201        (with-open-file (str ,file) (clnl-model:read-from-nlogo str))
202        :netlogo-callback (lambda (f) (setf callback f))))
203      (when ,commands (funcall callback ,commands))
204      (format nil "~A~A"
205       (clnl-nvm:export-world)
206       (checksum-world))))
207    "bin/runcmd.scala"
208    (format nil "~A@#$#@#$#@@#$#@#$#@@#$#@#$#@~A" ,commands ,file)))
209
210 (defmacro defviewtest (name commands checksum)
211  `(defsimpletest
212    (format nil "Simple View - ~A" ,name)
213    (lambda ()
214     (clnl:boot "resources/empty55.nlogo")
215     (clnl:run-commands ,commands)
216     (let
217      ((viewsum (checksum-view)))
218      (when (not (checksum= ,checksum viewsum))
219       (format t "~c[1;35m-- For ~A, got ~A but expected ~A~c[0m~%" #\Esc ,name viewsum ,checksum #\Esc))
220      (checksum= ,checksum (checksum-view))))
221    (lambda ()
222     (clnl:boot "resources/empty55.nlogo")
223     (clnl:run-commands ,commands)
224     (save-view-to-ppm)
225     (format nil "~A" (checksum-view)))
226    ""
227    (format nil "~A~%" ,commands)))
228
229 (defun checksum-world ()
230  (format nil "~{~2,'0X~}"
231   (map 'list #'identity
232    (ironclad:digest-sequence
233     :sha1
234     (map '(vector (unsigned-byte 8)) #'char-code (clnl-nvm:export-world))))))
235
236 (defun checksum-view ()
237  (format nil "~{~2,'0X~}"
238   (map 'list #'identity
239    (ironclad:digest-sequence :sha1 (coerce (clnl-interface:export-view) '(vector (unsigned-byte 8)))))))
240
241 (defun save-view-to-ppm ()
242  (let
243   ((height 143) (width 143)) ; hardcoded in interface, hardcoded here, cry for me
244   (with-open-file (str "cl.ppm"
245                    :direction :output
246                    :if-exists :supersede
247                    :if-does-not-exist :create
248                    :element-type '(unsigned-byte 8))
249    (write-sequence (map 'vector #'char-code (format nil "P6~%")) str)
250    (write-sequence (map 'vector #'char-code (format nil "143 143~%")) str)
251    (write-sequence (map 'vector #'char-code (format nil "255~%")) str)
252    (let
253     ((image-data (clnl-interface:export-view)))
254     (dotimes (i width)
255      (dotimes (j height)
256       (write-byte (aref image-data (+ 0 (* 4 (+ (* (- (1- height) i) width) j)))) str)
257       (write-byte (aref image-data (+ 1 (* 4 (+ (* (- (1- height) i) width) j)))) str)
258       (write-byte (aref image-data (+ 2 (* 4 (+ (* (- (1- height) i) width) j)))) str)))))))
259
260 (defun run ()
261  (loop
262   :for str := (progn (format t "> ") (force-output) (read-line))
263   :while str
264   :do (progn (asdf:load-system :clnl-test) (run-tests-matching str))))