Commit | Line | Data |
---|---|---|
e29834b8 MW |
1 | ### -*-autotest-*- |
2 | ### | |
3 | ### Test script for `runlisp' | |
4 | ### | |
5 | ### (c) 2020 Mark Wooding | |
6 | ### | |
7 | ||
8 | ###----- Licensing notice --------------------------------------------------- | |
9 | ### | |
10 | ### This file is part of Runlisp, a tool for invoking Common Lisp scripts. | |
11 | ### | |
12 | ### Runlisp is free software: you can redistribute it and/or modify it | |
13 | ### under the terms of the GNU General Public License as published by the | |
14 | ### Free Software Foundation; either version 3 of the License, or (at your | |
15 | ### option) any later version. | |
16 | ### | |
17 | ### Runlisp is distributed in the hope that it will be useful, but WITHOUT | |
18 | ### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | ### FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | ### for more details. | |
21 | ### | |
22 | ### You should have received a copy of the GNU General Public License | |
23 | ### along with Runlisp. If not, see <https://www.gnu.org/licenses/>. | |
24 | ||
25 | m4_define([RUNLISP_PATH], [$abs_top_builddir/runlisp]) | |
26 | ||
27 | m4_define([_FOREACH], [dnl | |
28 | m4_if([$#], [1], [_foreach_func($1)], | |
29 | [_foreach_func($1)[]_FOREACH(m4_shift($@))])]) | |
30 | m4_define([FOREACH], [dnl | |
31 | m4_pushdef([_foreach_func], [$2])dnl | |
32 | _FOREACH($1)[]dnl | |
33 | m4_popdef([_foreach_func])]) | |
34 | ||
35 | m4_define([LISP_SYSTEMS], | |
36 | [sbcl, sbcl/noimage, | |
37 | ccl, ccl/noimage, | |
38 | clisp, clisp/noimage, | |
39 | ecl, ecl/noimage, | |
40 | cmucl, cmucl/noimage, | |
41 | abcl, abcl/noimage]) | |
42 | ||
8996f767 MW |
43 | m4_define([SETUP_RUNLISP_ENV], |
44 | [RUNLISP_SYSCONFIG=$abs_top_srcdir/runlisp-base.conf; export RUNLISP_SYSCONFIG | |
45 | RUNLISP_SYSCONFIG_DIR=/notexist; export RUNLISP_SYSCONFIG_DIR | |
46 | RUNLISP_IMAGEDIR=$abs_top_builddir; export RUNLISP_IMAGEDIR | |
47 | RUNLISP_EVAL=$abs_top_srcdir/eval.lisp; export RUNLISP_EVAL | |
48 | unset RUNLISP_USERCONFIG | |
49 | ]) | |
50 | ||
e29834b8 | 51 | m4_define([PREPARE_LISP_TEST], |
8996f767 MW |
52 | [SETUP_RUNLISP_ENV |
53 | lisp=$1 | |
2c39b5e6 | 54 | LISP=$m4_translit(m4_bpatsubst([$1], [/.*$], []), [a-z], [A-Z]) |
e29834b8 MW |
55 | AT_SKIP_IF([test "x$LISP" = x]) |
56 | case $lisp in | |
57 | */*) opt=${lisp#*/} lisp=${lisp%%/*} ;; | |
58 | *) opt="" ;; | |
59 | esac | |
8996f767 | 60 | case /$opt/ in */noimage/*) RUNLISP_IMAGEDIR=./notexist ;; esac]) |
e29834b8 MW |
61 | |
62 | m4_define([WHICH_LISP], | |
63 | [(or #+sbcl "sbcl" #+ccl "ccl" #+clisp "clisp" | |
64 | #+ecl "ecl" #+cmu "cmucl" #+abcl "abcl" | |
65 | "unknown")]) | |
66 | ||
67 | m4_define([NL], [ | |
68 | ]) | |
69 | ||
e29834b8 MW |
70 | ###-------------------------------------------------------------------------- |
71 | ### A basic smoke test. | |
72 | ||
73 | ## Check that the system basically works, by running a trivial test program. | |
74 | ## Also try to verify that we're not running user or site startup code, | |
75 | ## though this is hard to do in general. | |
76 | FOREACH([LISP_SYSTEMS], | |
77 | [AT_SETUP([$1 smoke]) | |
78 | AT_KEYWORDS([script smoke $1]) | |
79 | PREPARE_LISP_TEST([$1]) | |
80 | ||
81 | ## Prepare a user-init file which will break the test if it's run by printing | |
82 | ## something unexpected. | |
83 | mkdir HOME | |
84 | case $lisp in | |
85 | sbcl) initfile=.sbclrc ;; | |
86 | ccl) initfile=.ccl-init.lisp ;; | |
87 | clisp) initfile=.clisprc.lisp ;; | |
88 | ecl) initfile=.eclrc ;; | |
89 | cmucl) initfile=.cmucl-init.lisp ;; | |
90 | abcl) initfile=.abclrc ;; | |
91 | esac | |
92 | cat >HOME/$initfile <<EOF | |
93 | (format t "*** I should not be seen~%") | |
94 | EOF | |
95 | HOME=$(pwd)/HOME; export HOME | |
96 | ||
97 | ## Prepare the script. | |
98 | cat >test-script <<EOF | |
99 | [#!] RUNLISP_PATH -L$lisp | |
100 | ||
101 | ;; Print a greeting to \`*standard-output*', identifying the Lisp system, so | |
102 | ;; that we can tell whether we called the right one. | |
103 | (format t "Hello from ~A (~A)!~%" (lisp-implementation-type) WHICH_LISP) | |
104 | ||
105 | #! this should be a comment everywhere | |
106 | ||
107 | ;; Make sure that \`*error-output*' is hooked up properly. | |
108 | (format *error-output* "to stderr~%") | |
109 | ||
110 | ;; Make sure that \`*standard-input*' is hooked up properly, by reading a line | |
111 | ;; and echoing it. | |
112 | (format t "from stdin: ~S~%" (read-line)) | |
113 | ||
114 | ;; Check that \`:runlisp-script' is set in \`*features*'. If not, \`assert' | |
115 | ;; will at least write a complaint to some stream, which will fail the test. | |
116 | (assert (member :runlisp-script *features*)) | |
117 | ||
118 | ;; Check that there are no symbols present (interned or imported) in the | |
119 | ;; \`common-lisp-user' package. Obviously, we must avoid interning any | |
120 | ;; ourselves. Alas, ABCL and ECL pollute \`cl-user' out of the box. (ECL | |
8996f767 MW |
121 | ;; does this deliberately; ABCL's ``adjoin.lisp' lacks an \`in-package' |
122 | ;; form.) | |
e29834b8 MW |
123 | (let ((#1=#:syms (sort (loop :for #2=#:s :being :the :present-symbols |
124 | :of *package* | |
125 | :collect #2#) | |
126 | #'string<))) | |
127 | (format t "package \`~A' [~:[ok~;has unexpected symbols ~:*~S~]]~%" | |
128 | (package-name *package*) #1#)) | |
129 | ||
130 | ;; Print the program name and command-line arguments. | |
131 | (format t "program name = ~S~%~ | |
132 | arguments = ~:S~%" | |
133 | (uiop:argv0) | |
134 | uiop:*command-line-arguments*) | |
135 | EOF | |
136 | chmod +x test-script | |
137 | ||
138 | case $lisp in | |
139 | sbcl) impl="SBCL" ;; | |
140 | ccl) impl="Clozure Common Lisp" ;; | |
141 | clisp) impl="CLISP" ;; | |
142 | ecl) impl="ECL" ;; | |
143 | cmucl) impl="CMU Common Lisp" ;; | |
144 | abcl) impl="Armed Bear Common Lisp" ;; | |
145 | *) AT_FAIL_IF([:]) ;; | |
146 | esac | |
147 | ||
148 | ## Prepare an input file. | |
149 | echo some random text >stdin | |
150 | ||
151 | ## Prepare the reference stdout and stderr. | |
152 | cat >stdout.ref <<EOF | |
153 | Hello from $impl ($lisp)! | |
154 | from stdin: "some random text" | |
155 | package \`COMMON-LISP-USER' ok | |
156 | program name = "./test-script" | |
157 | arguments = ("--eval" "nonsense" "--" "more" "args" "here") | |
158 | EOF | |
159 | cat >stderr.ref <<EOF | |
160 | to stderr | |
161 | EOF | |
162 | ||
163 | AT_CHECK([echo "lisp=$lisp opt=$opt"; env | grep RUNLISP | sort],, [stdout]) | |
164 | AT_CHECK([./test-script --eval nonsense -- more args here <stdin],, | |
165 | [stdout], [stderr]) | |
166 | AT_CHECK([diff -u stdout.ref stdout]) | |
167 | AT_CHECK([diff -u stderr.ref stderr]) | |
168 | AT_CLEANUP]) | |
169 | ||
170 | ###-------------------------------------------------------------------------- | |
171 | ### Check error handling. | |
172 | ||
173 | FOREACH([LISP_SYSTEMS], | |
174 | [AT_SETUP([$1 errors]) | |
175 | AT_KEYWORDS([script error $1]) | |
176 | PREPARE_LISP_TEST([$1]) | |
177 | ||
178 | ## A simple script which signals an error without catching it. | |
179 | cat >test <<EOF | |
180 | [#!] RUNLISP_PATH -L$lisp | |
181 | (error "just kill me now") | |
182 | EOF | |
183 | chmod +x test | |
184 | ||
185 | ## As long as it exits with a nonzero status, I'm happy. Some Lisps | |
186 | ## desperately want to drop the user into an interactive debugger, which is | |
187 | ## possibly useful for a developer, but an end user is now faced with a | |
188 | ## confusing internal error message /and/ a confusing prompt which won't go | |
189 | ## away. The output may still be confusing and (certainly in CCL's case) | |
190 | ## voluminous, but that's not significantly worse than Tcl or Java. | |
191 | ./test >out >err; rc=$? | |
192 | AT_FAIL_IF([test $rc = 0]) | |
193 | ||
194 | AT_CLEANUP]) | |
195 | ||
196 | ###-------------------------------------------------------------------------- | |
197 | ### Check eval mode. | |
198 | ||
199 | ### Eval mode is implemented centrally through a script, so we don't need to | |
200 | ### test it separately for each Lisp implementation. | |
201 | ||
202 | AT_SETUP([eval mode]) | |
203 | AT_KEYWORDS([eval common]) | |
8996f767 | 204 | SETUP_RUNLISP_ENV |
e29834b8 MW |
205 | |
206 | ## A very basic smoke test. | |
207 | AT_CHECK([RUNLISP_PATH -e '(format t "Just another Lisp hacker!~%")'],, | |
208 | [Just another Lisp hacker! | |
209 | ]) | |
210 | ||
211 | ## The `:runlisp-script' keyword should /not/ be in `*features*'. | |
212 | traceon | |
05a9f820 | 213 | AT_CHECK([RUNLISP_PATH -d '(find :runlisp-script *features*)'],, [NIL |
e29834b8 MW |
214 | ]) |
215 | ||
216 | ## Check a mixture of all the kinds of evaluation. We'll need a stunt script | |
217 | ## to make this work. Also check that the individual forms are read and | |
218 | ## evaluated one at a time, so that each one can affect the way the reader | |
219 | ## interprets the next. | |
220 | cat >script.lisp <<EOF | |
221 | #! just want to check that Lisp doesn't choke on a shebang line here | |
222 | (format t "And we're running the script...~%~ | |
223 | Command-line arguments: ~:S~%~ | |
224 | Symbols in package \`~A': ~:S~%" | |
225 | uiop:*command-line-arguments* | |
226 | (package-name *package*) | |
227 | (sort (loop :for #2=#:s :being :the :present-symbols :of *package* | |
228 | :collect #2#) | |
229 | #'string<)) | |
230 | EOF | |
231 | AT_CHECK([RUNLISP_PATH \ | |
8b79df0a MW |
232 | -e '(defpackage [#:]runlisp-test (:export [#:]foo [#:]bar)) |
233 | (defvar runlisp-test:foo 1) | |
234 | (defvar runlisp-test:bar "stoat!")' \ | |
05a9f820 MW |
235 | -d runlisp-test:foo \ |
236 | -d runlisp-test:bar \ | |
d2dbcc6f | 237 | -p runlisp-test:bar \ |
e29834b8 MW |
238 | -e '(incf runlisp-test:foo)' \ |
239 | -l script.lisp \ | |
05a9f820 | 240 | -d runlisp-test:foo \ |
e29834b8 MW |
241 | -- -e one two three],, |
242 | [1 | |
8b79df0a | 243 | "stoat!" |
d2dbcc6f | 244 | stoat! |
e29834b8 MW |
245 | And we're running the script... |
246 | Command-line arguments: ("-e" "one" "two" "three") | |
247 | Symbols in package `COMMON-LISP-USER': () | |
248 | 2 | |
249 | ]) | |
250 | ||
251 | AT_CLEANUP | |
252 | ||
253 | ###-------------------------------------------------------------------------- | |
254 | ### Check Lisp system selection and preference work. | |
255 | ||
256 | AT_SETUP([preferences]) | |
257 | AT_KEYWORDS([prefs common]) | |
8996f767 | 258 | SETUP_RUNLISP_ENV |
e29834b8 MW |
259 | |
260 | ## Before we can make this happen, we need to decide on three Lisp systems, | |
261 | ## two of which actually work, and one other. These are ordered by startup | |
262 | ## speed. | |
263 | unset lisp0 lisp1 badlisp; win=nil | |
264 | set -- cmucl sbcl ccl clisp ecl abcl | |
265 | while :; do | |
266 | case $# in 0) break ;; esac | |
267 | lisp=$1; shift | |
268 | if RUNLISP_PATH -L$lisp -enil 2>/dev/null; then good=t; else good=nil; fi | |
269 | case ${lisp0+t},${badlisp+t},$good in | |
270 | ,*,t) lisp0=$lisp ;; | |
271 | t,*,t) lisp1=$lisp win=t; break ;; | |
272 | *,,nil) badlisp=$lisp ;; | |
273 | esac | |
274 | done | |
275 | AT_CHECK([case $win in nil) exit 77 ;; esac]) | |
276 | case ${badlisp+t} in t) ;; *) badlisp=$1 ;; esac | |
277 | BADLISP=$(echo $badlisp | tr a-z A-Z) | |
278 | eval $BADLISP=/notexist/definitely-wrong | |
279 | export $BADLISP | |
280 | echo Primary Lisp = $lisp0 | |
281 | echo Secondary Lisp = $lisp1 | |
282 | echo Bad Lisp = $badlisp | |
283 | ||
284 | ## Check that our selection worked. | |
05a9f820 MW |
285 | AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp0 -d 'WHICH_LISP'],, ["$lisp0"NL]) |
286 | AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp1 -d 'WHICH_LISP'],, ["$lisp1"NL]) | |
287 | AT_CHECK([RUNLISP_PATH -L$badlisp -d 'WHICH_LISP'], [127],, | |
de2e8025 | 288 | [runlisp: no acceptable Lisp systems found[]NL]) |
e29834b8 MW |
289 | |
290 | ## Unset all of the user preference mechanisms. | |
e29834b8 MW |
291 | here=$(pwd) |
292 | mkdir HOME config | |
293 | HOME=$here/HOME XDG_CONFIG_HOME=$here/config; export HOME XDG_CONFIG_HOME | |
294 | ||
295 | ## We generally take the first one listed that exists. | |
05a9f820 MW |
296 | AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp0,$lisp1 -d 'WHICH_LISP'],, ["$lisp0"NL]) |
297 | AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp1,$lisp0 -d 'WHICH_LISP'],, ["$lisp1"NL]) | |
298 | AT_CHECK_UNQUOTED([RUNLISP_PATH -L$badlisp,$lisp0,$lisp1 -d 'WHICH_LISP'],, | |
e29834b8 MW |
299 | ["$lisp0"NL]) |
300 | ||
301 | ## Check parsing of embedded options. | |
302 | for i in 0 1; do | |
303 | j=$(( 1 - $i )); eval lisp=\$lisp$i olisp=\$lisp$j | |
304 | cat >script$i <<EOF | |
305 | [#!] RUNLISP_PATH | |
306 | ;;; -z @RUNLISP: -L$lisp -*- -z -*- -L$olisp -- -z | |
307 | (prin1 WHICH_LISP) (terpri) | |
308 | EOF | |
309 | chmod +x script$i | |
310 | AT_CHECK_UNQUOTED([./script$i],, ["$lisp"NL]) | |
311 | done | |
312 | ||
313 | ## Preferences will override the order of acceptable implementations. | |
8996f767 MW |
314 | AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp,$lisp0 ./script0],, ["$lisp0"NL]) |
315 | AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp,$lisp0 ./script1],, ["$lisp0"NL]) | |
e29834b8 MW |
316 | |
317 | ## But doesn't affect the preference order of unmentioned Lisps. | |
8996f767 MW |
318 | AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp ./script0],, ["$lisp0"NL]) |
319 | AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp ./script1],, ["$lisp1"NL]) | |
e29834b8 MW |
320 | |
321 | ## Test configuration files and interactions with the environment. | |
8996f767 | 322 | for conf in HOME/.runlisp.conf config/runlisp.conf; do |
e29834b8 MW |
323 | for i in 0 1; do |
324 | j=$(( 1 - $i )); eval lisp=\$lisp$i olisp=\$lisp$j | |
325 | cat >$conf <<EOF | |
8996f767 MW |
326 | ;;; -*-conf-*- |
327 | prefer = $lisp | |
e29834b8 MW |
328 | EOF |
329 | ||
330 | ## Basic check. | |
331 | AT_CHECK_UNQUOTED([./script0],, ["$lisp"NL]) | |
332 | AT_CHECK_UNQUOTED([./script1],, ["$lisp"NL]) | |
333 | ||
8996f767 MW |
334 | ## Environment variable overrides. |
335 | AT_CHECK_UNQUOTED([RUNLISP_PREFER=$olisp ./script0],, ["$olisp"NL]) | |
336 | AT_CHECK_UNQUOTED([RUNLISP_PREFER=$olisp ./script1],, ["$olisp"NL]) | |
e29834b8 MW |
337 | |
338 | done | |
339 | rm -f $conf | |
340 | done | |
341 | ||
342 | ||
343 | ||
344 | AT_CLEANUP | |
345 | ||
346 | ###----- That's all, folks -------------------------------------------------- |