tests.at: Wrap an overly long comment line.
[runlisp] / tests.at
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
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
51 m4_define([PREPARE_LISP_TEST],
52 [SETUP_RUNLISP_ENV
53 lisp=$1
54 LISP=$m4_translit(m4_bpatsubst([$1], [/.*$], []), [a-z], [A-Z])
55 AT_SKIP_IF([test "x$LISP" = x])
56 case $lisp in
57 */*) opt=${lisp@%:@*/} lisp=${lisp%%/*} ;;
58 *) opt="" ;;
59 esac
60 case /$opt/ in */noimage/*) RUNLISP_IMAGEDIR=./notexist ;; esac])
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
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
111 ;; line 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
121 ;; does this deliberately; ABCL's ``adjoin.lisp' lacks an \`in-package'
122 ;; form.)
123 (let ((@%:@1=@%:@:syms (sort (loop :for @%:@2=@%:@:s :being
124 :the :present-symbols :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])
204 SETUP_RUNLISP_ENV
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
213 AT_CHECK([RUNLISP_PATH -d '(find :runlisp-script *features*)'],, [NIL
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
228 :the :present-symbols :of *package*
229 :collect @%:@2@%:@)
230 @%:@'string<))
231 EOF
232 AT_CHECK([RUNLISP_PATH \
233 -e '(defpackage @%:@:runlisp-test (:export @%:@:foo @%:@:bar))
234 (defvar runlisp-test:foo 1)
235 (defvar runlisp-test:bar "stoat!")' \
236 -d runlisp-test:foo \
237 -d runlisp-test:bar \
238 -p runlisp-test:bar \
239 -e '(incf runlisp-test:foo)' \
240 -l script.lisp \
241 -d runlisp-test:foo \
242 -- -e one two three],,
243 [1
244 "stoat!"
245 stoat!
246 And we're running the script...
247 Command-line arguments: ("-e" "one" "two" "three")
248 Symbols in package `COMMON-LISP-USER': ()
249 2
250 ])
251
252 AT_CLEANUP
253
254 ###--------------------------------------------------------------------------
255 ### Check Lisp system selection and preference work.
256
257 AT_SETUP([preferences])
258 AT_KEYWORDS([prefs common])
259 SETUP_RUNLISP_ENV
260
261 ## Before we can make this happen, we need to decide on three Lisp systems,
262 ## two of which actually work, and one other. These are ordered by startup
263 ## speed.
264 unset lisp0 lisp1 badlisp; win=nil
265 set -- cmucl sbcl ccl clisp ecl abcl
266 while :; do
267 case $@%:@ in 0) break ;; esac
268 lisp=$1; shift
269 if RUNLISP_PATH -L$lisp -enil 2>/dev/null; then good=t; else good=nil; fi
270 case ${lisp0+t},${badlisp+t},$good in
271 ,*,t) lisp0=$lisp ;;
272 t,*,t) lisp1=$lisp win=t; break ;;
273 *,,nil) badlisp=$lisp ;;
274 esac
275 done
276 AT_CHECK([case $win in nil) exit 77 ;; esac])
277 case ${badlisp+t} in t) ;; *) badlisp=$1 ;; esac
278 BADLISP=$(echo $badlisp | tr a-z A-Z)
279 eval $BADLISP=/notexist/definitely-wrong
280 export $BADLISP
281 echo Primary Lisp = $lisp0
282 echo Secondary Lisp = $lisp1
283 echo Bad Lisp = $badlisp
284
285 ## Check that our selection worked.
286 AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp0 -d 'WHICH_LISP'],, ["$lisp0"NL])
287 AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp1 -d 'WHICH_LISP'],, ["$lisp1"NL])
288 AT_CHECK([RUNLISP_PATH -L$badlisp -d 'WHICH_LISP'], [127],,
289 [runlisp: no acceptable Lisp systems found[]NL])
290
291 ## Unset all of the user preference mechanisms.
292 here=$(pwd)
293 mkdir HOME config
294 HOME=$here/HOME XDG_CONFIG_HOME=$here/config; export HOME XDG_CONFIG_HOME
295
296 ## We generally take the first one listed that exists.
297 AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp0,$lisp1 -d 'WHICH_LISP'],, ["$lisp0"NL])
298 AT_CHECK_UNQUOTED([RUNLISP_PATH -L$lisp1,$lisp0 -d 'WHICH_LISP'],, ["$lisp1"NL])
299 AT_CHECK_UNQUOTED([RUNLISP_PATH -L$badlisp,$lisp0,$lisp1 -d 'WHICH_LISP'],,
300 ["$lisp0"NL])
301
302 ## Check parsing of embedded options.
303 for i in 0 1; do
304 j=$(( 1 - $i )); eval lisp=\$lisp$i olisp=\$lisp$j
305 cat >script$i <<EOF
306 @%:@! RUNLISP_PATH
307 ;;; -z @RUNLISP: -L$lisp -*- -z -*- -L$olisp -- -z
308 (prin1 WHICH_LISP) (terpri)
309 EOF
310 chmod +x script$i
311 AT_CHECK_UNQUOTED([./script$i],, ["$lisp"NL])
312 done
313
314 ## Preferences will override the order of acceptable implementations.
315 AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp,$lisp0 ./script0],, ["$lisp0"NL])
316 AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp,$lisp0 ./script1],, ["$lisp0"NL])
317
318 ## But doesn't affect the preference order of unmentioned Lisps.
319 AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp ./script0],, ["$lisp0"NL])
320 AT_CHECK_UNQUOTED([RUNLISP_PREFER=$badlisp ./script1],, ["$lisp1"NL])
321
322 ## Test configuration files and interactions with the environment.
323 for conf in HOME/.runlisp.conf config/runlisp.conf; do
324 for i in 0 1; do
325 j=$(( 1 - $i )); eval lisp=\$lisp$i olisp=\$lisp$j
326 cat >$conf <<EOF
327 ;;; -*-conf-*-
328 prefer = $lisp
329 EOF
330
331 ## Basic check.
332 AT_CHECK_UNQUOTED([./script0],, ["$lisp"NL])
333 AT_CHECK_UNQUOTED([./script1],, ["$lisp"NL])
334
335 ## Environment variable overrides.
336 AT_CHECK_UNQUOTED([RUNLISP_PREFER=$olisp ./script0],, ["$olisp"NL])
337 AT_CHECK_UNQUOTED([RUNLISP_PREFER=$olisp ./script1],, ["$olisp"NL])
338
339 done
340 rm -f $conf
341 done
342
343 AT_CLEANUP
344
345 ###--------------------------------------------------------------------------
346 ### Implementation-specific tests.
347
348 AT_SETUP([specific sbcl])
349 AT_KEYWORDS([specific sbcl])
350 PREPARE_LISP_TEST([sbcl])
351
352 AT_CHECK([RUNLISP_PATH -e"(require 'sb-bsd-sockets)"])
353
354 AT_CLEANUP
355
356 ###----- That's all, folks --------------------------------------------------