1 ;;;; tests related to Lisp streams
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
14 (load "assertoid.lisp")
15 (use-package "ASSERTOID")
17 ;;; type errors for inappropriate stream arguments, fixed in
19 (with-test (:name
(make-two-way-stream type-error
))
20 (locally (declare (optimize (safety 3)))
21 (assert-error (make-two-way-stream (make-string-output-stream)
22 (make-string-output-stream))
24 (assert-error (make-two-way-stream (make-string-input-stream "foo")
25 (make-string-input-stream "bar"))
28 (with-test (:name
(make-echo-stream type-error
))
29 (locally (declare (optimize (safety 3)))
30 ;; the following two aren't actually guaranteed, because ANSI, as it
31 ;; happens, doesn't say "should signal an error" for
32 ;; MAKE-ECHO-STREAM. It's still good to have, but if future
33 ;; maintenance work causes this test to fail because of these
34 ;; MAKE-ECHO-STREAM clauses, consider simply removing these clauses
35 ;; from the test. -- CSR, 2002-10-06
36 (assert-error (make-echo-stream (make-string-output-stream)
37 (make-string-output-stream))
39 (assert-error (make-echo-stream (make-string-input-stream "foo")
40 (make-string-input-stream "bar"))
43 (with-test (:name
(make-concatenated-stream type-error
))
44 (locally (declare (optimize (safety 3)))
45 (assert-error (make-concatenated-stream
46 (make-string-output-stream)
47 (make-string-input-stream "foo"))
50 ;;; bug 225: STRING-STREAM was not a class
53 `(defgeneric bug225
(s)
54 ,@(mapcar (lambda (class)
55 `(:method
:around
((s ,class
))
56 (cons ',class
(call-next-method))))
57 '(stream string-stream
58 sb-impl
::string-input-stream
59 sb-impl
::string-output-stream
))
60 (:method
(class) nil
))))
63 (with-test (:name
(string-stream class
:bug-225
))
64 (assert (equal (bug225 (make-string-input-stream "hello"))
65 '(sb-impl::string-input-stream string-stream stream
)))
66 (assert (equal (bug225 (make-string-output-stream))
67 '(sb-impl::string-output-stream string-stream stream
))))
70 ;;; improper buffering on (SIGNED-BYTE 8) streams (fixed by David Lichteblau):
71 (with-test (:name
(write-byte (unsigned-byte 8) read-byte
(signed-byte 8)))
72 (let ((p "signed-byte-8-test.data"))
75 :element-type
'(unsigned-byte 8)
76 :if-exists
:supersede
)
78 (with-open-file (s p
:element-type
'(signed-byte 8))
79 (assert (= (read-byte s
) -
1)))
82 ;;; :IF-EXISTS got :ERROR and NIL the wrong way round (reported by
84 (with-test (:name
(open :if-exists
:error
))
85 (let* ((p "this-file-will-exist")
86 (stream (open p
:direction
:output
:if-exists
:error
)))
87 (assert (null (with-open-file (s p
:direction
:output
:if-exists nil
) s
)))
89 (with-open-file (s p
:direction
:output
:if-exists
:error
)))
93 (with-test (:name
(read-byte make-string-input-stream type-error
))
94 (assert-error (read-byte (make-string-input-stream "abc"))
97 (with-test (:name
(:default
:element-type read-byte error
))
98 (assert-error (with-open-file (s "/dev/zero")
101 #+win32 sb-int
:simple-file-error
))
103 ;;; bidirectional streams getting confused about their position
104 (with-test (:name
(:direction
:io
))
105 (let ((p "bidirectional-stream-test"))
106 (with-open-file (s p
:direction
:output
:if-exists
:supersede
)
107 (with-standard-io-syntax
108 (format s
"~S ~S ~S~%" 'these
'are
'symbols
)))
109 (with-open-file (s p
:direction
:io
:if-exists
:overwrite
)
111 (with-standard-io-syntax
113 (with-open-file (s p
)
114 (let ((line (read-line s
))
115 (want "THESE INSERTMBOLS"))
116 (assert (equal line want
))))
119 ;;; :DIRECTION :IO didn't work on non-existent pathnames
120 (with-test (:name
(with-open-file :direction
:io
:non-existent-pathname
))
121 (let ((p "direction-io-test"))
122 (ignore-errors (delete-file p
))
123 (with-open-file (s p
:direction
:io
)
126 (file-position s
:start
)
127 (assert (char= (read-char s
) #\
1)))
130 ;;; FILE-POSITION on broadcast-streams is mostly uncontroversial
131 (with-test (:name
(file-position broadcast-stream
1))
132 (assert (= 0 (file-position (make-broadcast-stream))))
133 (assert (file-position (make-broadcast-stream) :start
))
134 (assert (file-position (make-broadcast-stream) 0))
135 (assert (not (file-position (make-broadcast-stream) 1)))
136 (let ((s (make-broadcast-stream)))
138 (assert (not (file-position s
1)))
139 (assert (= 0 (file-position s
)))))
141 (with-test (:name
(file-position broadcast-stream
2))
142 (let ((p "broadcast-stream-test"))
143 (ignore-errors (delete-file p
))
144 (with-open-file (f p
:direction
:output
)
145 (let ((s (make-broadcast-stream f
)))
146 (assert (= 0 (file-position s
)))
147 (assert (file-position s
:start
))
148 (assert (file-position s
0))
150 (assert (= 1 (file-position s
))) ; unicode...
151 (assert (file-position s
0))))
154 ;;; CLOSING a non-new streams should not delete them, and superseded
155 ;;; files should be restored.
156 (with-test (:name
:test-file-for-close-should-not-delete
:fails-on
:win32
)
157 (let ((test "test-file-for-close-should-not-delete"))
158 (macrolet ((test-mode (mode)
160 (catch :close-test-exit
161 (with-open-file (f test
:direction
:output
:if-exists
,mode
)
162 (write-line "test" f
)
163 (throw :close-test-exit t
)))
164 (assert (and (probe-file test
) ,mode
)))))
167 (with-open-file (f test
:direction
:output
)
168 (write-line "test" f
))
170 (test-mode :overwrite
)
171 ;; FIXME: We really should recover supersede files as well, according to
172 ;; CLOSE in CLHS, but at the moment we don't.
173 ;; (test-mode :supersede)
175 (test-mode :rename-and-delete
))
176 (when (probe-file test
)
177 (delete-file test
))))))
179 ;;; test for read-write invariance of signed bytes, from Bruno Haible
180 ;;; cmucl-imp 2004-09-06
181 (defun bin-stream-test (&key
(size (integer-length most-positive-fixnum
))
182 (type 'unsigned-byte
) (file-name "stream-impure.tmp")
184 (bytes (if (eq type
'signed-byte
)
185 (loop :repeat num-bytes
:collect
186 (- (random (ash 1 size
))
188 (loop :repeat num-bytes
:collect
189 (random (ash 1 size
))))))
190 (with-open-file (foo file-name
:direction
:output
:if-exists
:supersede
191 :element-type
(list type size
))
193 (write-byte byte foo
)))
195 (with-open-file (foo file-name
:direction
:input
196 :element-type
(list type size
))
197 (list (stream-element-type foo
) (file-length foo
) bytes
198 (loop :for byte
:in bytes
:for nb
= (read-byte foo
) :collect nb
199 :unless
(= nb byte
) :do
200 (flet ((by-out (sz by
)
201 (format nil
"~v,'0,' ,4:b"
202 (+ sz
(floor sz
4)) by
)))
203 (error "~& * [(~s ~s)] ~a != ~a~%" type size
204 (by-out size byte
) (by-out size nb
))))))
205 (delete-file file-name
)))
207 (with-test (:name
(:element-type signed-byte write-byte write-byte
))
208 (loop for size from
2 to
40 do
209 (bin-stream-test :size size
:type
'signed-byte
)))
211 ;;; Check READ-SEQUENCE signals a TYPE-ERROR when the sequence can't
212 ;;; contain a stream element.
214 ;;; These tests check READ-SEQUENCE correctness, not whether the fast
215 ;;; or slow paths are being taken for each element type. To check the
216 ;;; fast or slow paths, trace ANSI-STREAM-READ-BYTE (slow path) and/or
219 ;;; (trace sb-impl::ansi-stream-read-byte sb-impl::read-n-bytes)
221 ;;; The order should be ANSI-STREAM-READ-BYTE, READ-N-BYTES,
222 ;;; READ-N-BYTES, ANSI-STREAM-READ-BYTE, ANSI-STREAM-READ-BYTE.
224 (with-test (:name
(read-sequence type-error
))
225 (let ((pathname "read-sequence.data"))
227 ;; Create the binary data.
228 (with-open-file (stream pathname
230 :if-exists
:supersede
231 :element-type
'(unsigned-byte 8))
232 (write-byte 255 stream
))
234 ;; Check the slow path for generic vectors.
235 (let ((sequence (make-array 1)))
236 (with-open-file (stream pathname
238 :element-type
'(unsigned-byte 8))
239 (read-sequence sequence stream
)
240 (assert (equalp sequence
#(255)))))
242 (let ((sequence (make-array 1)))
243 (with-open-file (stream pathname
245 :external-format
:latin-1
246 :element-type
'character
)
247 (read-sequence sequence stream
)
248 (assert (equalp sequence
#(#.
(code-char 255))))))
250 ;; Check the fast path works for (UNSIGNED-BYTE 8) and (SIGNED-BYTE
252 (let ((sequence (make-array 1 :element-type
'(unsigned-byte 8))))
253 (with-open-file (stream pathname
255 :element-type
'(unsigned-byte 8))
256 (read-sequence sequence stream
)
257 (assert (equalp sequence
#(255)))))
259 (let ((sequence (make-array 1 :element-type
'(signed-byte 8))))
260 (with-open-file (stream pathname
262 :element-type
'(signed-byte 8))
263 (read-sequence sequence stream
)
264 (assert (equalp sequence
#(-1)))))
266 ;; A bivalent stream can be read to a unsigned-byte vector, a
267 ;; string, or a generic vector
269 (let ((sequence (make-array 1 :element-type
'(unsigned-byte 8))))
270 (with-open-file (stream pathname
272 :element-type
:default
)
273 (read-sequence sequence stream
)
274 (assert (equalp sequence
#(255)))))
276 (let ((sequence (make-array 1 :element-type
'character
)))
277 (with-open-file (stream pathname
279 :external-format
:latin-1
280 :element-type
:default
)
281 (read-sequence sequence stream
)
282 (assert (equalp sequence
#(#.
(code-char 255))))))
284 (let ((sequence (make-array 1)))
285 (with-open-file (stream pathname
287 :external-format
:latin-1
288 :element-type
:default
)
289 (read-sequence sequence stream
)
290 (assert (equalp sequence
#(#.
(code-char 255))))))
292 ;; Check that a TYPE-ERROR is signalled for incompatible (sequence,
295 (let ((sequence (make-array 1 :element-type
'(signed-byte 8))))
296 (with-open-file (stream pathname
298 :element-type
'(unsigned-byte 8))
300 (read-sequence sequence stream
)
301 (error "READ-SEQUENCE didn't signal an error"))
302 (type-error (condition)
303 (assert (= (type-error-datum condition
) 255))
304 (assert (subtypep (type-error-expected-type condition
)
305 '(signed-byte 8)))))))
307 (let ((sequence (make-array 1 :element-type
'(unsigned-byte 8))))
308 (with-open-file (stream pathname
310 :element-type
'(signed-byte 8))
312 (read-sequence sequence stream
)
313 (error "READ-SEQUENCE didn't signal an error"))
314 (type-error (condition)
315 (assert (= (type-error-datum condition
) -
1))
316 (assert (subtypep (type-error-expected-type condition
)
317 '(unsigned-byte 8)))))))
319 ;; Can't read a signed-byte from a bivalent stream
321 (let ((sequence (make-array 1 :element-type
'(signed-byte 8))))
322 (with-open-file (stream pathname
324 :external-format
:latin1
325 :element-type
:default
)
327 (read-sequence sequence stream
)
328 (error "READ-SEQUENCE didn't signal an error"))
329 (type-error (condition)
330 (assert (eql (type-error-datum condition
) 255))
331 (assert (subtypep (type-error-expected-type condition
)
332 '(signed-byte 8)))))))
333 (delete-file pathname
)))
335 ;;; Check WRITE-SEQUENCE signals a TYPE-ERROR when the stream can't
336 ;;; write a sequence element.
338 ;;; These tests check WRITE-SEQUENCE correctness, not whether the fast
339 ;;; or slow paths are being taken for each element type. See the
340 ;;; READ-SEQUENCE tests above for more information.
342 ;;; (trace sb-impl::output-unsigned-byte-full-buffered sb-impl::output-signed-byte-full-buffered sb-impl::output-raw-bytes)
344 (with-test (:name
(write-sequence type-error
))
345 (let ((pathname "write-sequence.data")
346 (generic-sequence (make-array 1 :initial-contents
'(255)))
347 (generic-character-sequence (make-array 1 :initial-element
#\a))
348 (generic-mixed-sequence (make-array 2 :initial-element
#\a))
349 (string (make-array 1 :element-type
'character
350 :initial-element
(code-char 255)))
351 (unsigned-sequence (make-array 1
352 :element-type
'(unsigned-byte 8)
353 :initial-contents
'(255)))
354 (signed-sequence (make-array 1
355 :element-type
'(signed-byte 8)
356 :initial-contents
'(-1))))
358 (setf (aref generic-mixed-sequence
1) 255)
360 ;; Check the slow path for generic vectors.
361 (with-open-file (stream pathname
363 :if-exists
:supersede
364 :element-type
'(unsigned-byte 8))
365 (write-sequence generic-sequence stream
))
367 (with-open-file (stream pathname
369 :if-exists
:supersede
370 :element-type
'character
)
371 (write-sequence generic-character-sequence stream
))
373 ;; Check the fast path for unsigned and signed vectors.
374 (with-open-file (stream pathname
376 :if-exists
:supersede
377 :element-type
'(unsigned-byte 8))
378 (write-sequence unsigned-sequence stream
))
380 (with-open-file (stream pathname
382 :if-exists
:supersede
383 :element-type
'(signed-byte 8))
384 (write-sequence signed-sequence stream
))
386 ;; Bivalent streams on unsigned-byte vectors, strings, and a simple
387 ;; vector with mixed characters and bytes
389 (with-open-file (stream pathname
391 :if-exists
:supersede
392 :element-type
:default
)
393 (write-sequence unsigned-sequence stream
))
395 (with-open-file (stream pathname
397 :external-format
:latin-1
398 :if-exists
:supersede
399 :element-type
:default
)
400 (write-sequence string stream
))
402 (with-open-file (stream pathname
404 :external-format
:latin-1
405 :if-exists
:supersede
406 :element-type
:default
)
407 (write-sequence generic-mixed-sequence stream
))
409 ;; Check a TYPE-ERROR is signalled for unsigned and signed vectors
410 ;; which are incompatible with the stream element type.
411 (with-open-file (stream pathname
413 :if-exists
:supersede
414 :element-type
'(signed-byte 8))
416 (write-sequence unsigned-sequence stream
)
417 (error "WRITE-SEQUENCE didn't signal an error"))
418 (type-error (condition)
419 (assert (= (type-error-datum condition
) 255))
420 (assert (subtypep (type-error-expected-type condition
)
421 '(signed-byte 8))))))
423 (with-open-file (stream pathname
425 :if-exists
:supersede
426 :element-type
'(unsigned-byte 8))
428 (write-sequence signed-sequence stream
)
429 (error "WRITE-SEQUENCE didn't signal an error"))
430 (type-error (condition)
431 (assert (= (type-error-datum condition
) -
1))
432 (assert (subtypep (type-error-expected-type condition
)
433 '(unsigned-byte 8))))))
435 (with-open-file (stream pathname
437 :if-exists
:supersede
438 :element-type
:default
)
440 (write-sequence signed-sequence stream
)
441 (error "WRITE-SEQUENCE didn't signal an error"))
442 (type-error (condition)
443 (assert (= (type-error-datum condition
) -
1))
444 (assert (subtypep (type-error-expected-type condition
)
445 '(unsigned-byte 8))))))
447 (delete-file pathname
)))
449 ;;; writing looong lines. takes way too long and way too much space
450 ;;; to test on 64 bit platforms
451 (with-test (:name
(:write-char
:long-lines
:stream-ouput-column
)
453 (let ((test "long-lines-write-test.tmp"))
455 (with-open-file (f test
457 :external-format
:ascii
458 :element-type
'character
459 :if-does-not-exist
:create
460 :if-exists
:supersede
)
461 (let* ((n (truncate most-positive-fixnum
16))
464 (buffer (make-string n
)))
468 (write-sequence buffer f
))
469 (assert (= p
(sb-impl::fd-stream-output-column f
)))
471 (assert (= (+ 1 p
) (sb-impl::fd-stream-output-column f
)))
472 (assert (typep p
'bignum
))))
473 (when (probe-file test
)
474 (delete-file test
)))))
476 ;;; read-sequence misreported the amount read and lost position
477 (with-test (:name
(read-sequence :read-elements
))
478 (let ((string (make-array (* 3 sb-impl
::+ansi-stream-in-buffer-length
+)
479 :element-type
'character
)))
480 (dotimes (i (length string
))
481 (setf (char string i
) (code-char (mod i char-code-limit
))))
482 (with-open-file (f "read-sequence-character-test-data.tmp"
483 :if-exists
:supersede
485 :external-format
:utf-8
)
486 (write-sequence string f
))
488 (with-open-file (f "read-sequence-character-test-data.tmp"
489 :if-does-not-exist
:error
491 :external-format
:utf-8
)
492 (let ((buffer (make-array 128 :element-type
'character
))
494 (with-output-to-string (datum)
495 (loop for n-read
= (read-sequence buffer f
)
496 do
(write-sequence buffer datum
:start
0 :end n-read
)
497 (assert (<= (incf total n-read
) (length string
)))
498 while
(and (= n-read
128))))))))
499 (assert (equal copy string
)))
500 (delete-file "read-sequence-character-test-data.tmp")))
502 ;;; ANSI-STREAM-OUTPUT-STREAM-P used to assume that a SYNONYM-STREAM's
503 ;;; target was an ANSI stream, but it could be a user-defined stream,
504 ;;; e.g., a SLIME stream.
505 (defclass user-output-stream
(fundamental-output-stream)
508 (with-test (:name
(make-synonym-stream :user-defined output-stream-p
))
509 (let ((*stream
* (make-instance 'user-output-stream
)))
510 (declare (special *stream
*))
511 (with-open-stream (stream (make-synonym-stream '*stream
*))
512 (assert (output-stream-p stream
)))))
514 (defclass user-input-stream
(fundamental-input-stream)
517 (with-test (:name
(make-synonym-stream :user-defined input-stream-p
))
518 (let ((*stream
* (make-instance 'user-input-stream
)))
519 (declare (special *stream
*))
520 (with-open-stream (stream (make-synonym-stream '*stream
*))
521 (assert (input-stream-p stream
)))))
523 ;;; READ-LINE on ANSI-STREAM did not return T for the last line
524 ;;; (reported by Yoshinori Tahara)
525 (with-test (:name
(read-line :last-line
))
526 (let ((pathname "test-read-line-eol"))
527 (with-open-file (out pathname
:direction
:output
:if-exists
:supersede
)
529 (let ((result (with-open-file (in pathname
)
530 (list (multiple-value-list (read-line in nil nil
))
531 (multiple-value-list (read-line in nil nil
))
532 (multiple-value-list (read-line in nil nil
))))))
533 (delete-file pathname
)
534 (assert (equal result
'(("a" nil
) ("b" t
) (nil t
)))))))
536 ;;; READ-LINE used to work on closed streams because input buffers were left in place
537 (with-test (:name
(close read-line
:bug-425
))
539 (let ((f (open "stream.impure.lisp" :direction
:input
)))
540 (assert (stringp (read-line f
)))
542 (assert-error (read-line f
) sb-int
:closed-stream-error
))
544 (let ((f (open "stream.impure.lisp" :direction
:input
)))
545 (assert (stringp (read-line f nil nil
)))
547 (assert-error (read-line f
) sb-int
:closed-stream-error
)))
549 (with-test (:name
:regression-1.0
.12.22)
550 (with-open-file (s "stream.impure.lisp" :direction
:input
)
551 (let ((buffer (make-string 20)))
552 (assert (= 2 (read-sequence buffer s
:start
0 :end
2)))
553 (assert (= 3 (read-sequence buffer s
:start
2 :end
3)))
554 (file-position s
:end
)
555 (assert (= 3 (read-sequence buffer s
:start
3))))))
557 ;;; In 1.0.27 (and also 0.9.16; presumably in between, too), binary
558 ;;; input operations on a bivalent stream did something bad after
559 ;;; unread-char: READ-BYTE would return the character, and
560 ;;; READ-SEQUENCE into a byte buffer would lose when attempting to
561 ;;; store the character in the vector.
562 (with-test (:name
(:bivalent stream unread-char read-byte
))
563 (let ((pathname "bivalent-stream-unread-char-test.tmp"))
564 (with-open-file (s pathname
565 :element-type
:default
566 :direction
:io
:if-exists
:rename
)
568 (file-position s
:start
)
569 (unread-char (read-char s
) s
)
570 (assert (integerp (read-byte s
))))
571 (delete-file pathname
)))
573 (with-test (:name
(:bivalent stream unread-char read-sequence
))
574 (let ((pathname "bivalent-stream-unread-char-test.tmp"))
575 (with-open-file (s pathname
576 :element-type
:default
577 :direction
:io
:if-exists
:rename
)
579 (file-position s
:start
)
580 (unread-char (read-char s
) s
)
581 (assert (let ((buffer (make-array 10 :element-type
'(unsigned-byte 8))))
582 (read-sequence buffer s
))))
583 (delete-file pathname
)))
585 (with-test (:name
(:bivalent stream unread-char read-byte
:utf8
)
586 :skipped-on
'(:not
:sb-unicode
))
587 (let ((pathname "bivalent-stream-unread-char-test.tmp"))
588 (with-open-file (s pathname
589 :element-type
:default
590 :direction
:io
:if-exists
:rename
591 :external-format
:utf8
)
592 (write-char (code-char 192) s
)
593 (file-position s
:start
)
594 (unread-char (read-char s
) s
)
595 (assert (integerp (read-byte s
))))
596 (delete-file pathname
)))
598 (with-test (:name
(:bivalent stream unread-char read-sequence
:utf8
)
599 :skipped-on
'(:not
:sb-unicode
))
600 (let ((pathname "bivalent-stream-unread-char-test.tmp"))
601 (with-open-file (s pathname
602 :element-type
:default
603 :direction
:io
:if-exists
:rename
604 :external-format
:utf8
)
605 (write-char (code-char 192) s
)
606 (file-position s
:start
)
607 (unread-char (read-char s
) s
)
608 (assert (let ((buffer (make-array 10 :element-type
'(unsigned-byte 8))))
609 (read-sequence buffer s
))))
610 (delete-file pathname
)))
612 (with-test (:name
(delete-file :on stream
))
613 (with-open-file (f "delete-file-on-stream-test.tmp"
618 (write-line "still open" f
)
619 (file-position f
:start
)
620 (assert (equal "still open" (read-line f
)))))
621 (assert (not (probe-file "delete-file-on-stream-test.tmp"))))
623 ;;; READ-CHAR-NO-HANG on bivalent streams (as returned by RUN-PROGRAM)
624 ;;; was wrong. CSR managed to promote the wrongness to all streams in
625 ;;; the 1.0.32.x series, breaking slime instantly.
626 (with-test (:name
(read-char :no-hang-after unread-char
) :skipped-on
:win32
)
627 (let* ((process (run-program "/bin/sh" '("-c" "echo a && sleep 10")
628 :output
:stream
:wait nil
))
629 (stream (process-output process
))
630 (char (read-char stream
)))
631 (assert (char= char
#\a))
632 (unread-char char stream
)
633 (assert (char= (read-char stream
) #\a))
634 (assert (char= (read-char stream
) #\Newline
))
635 (let ((time (get-universal-time)))
636 ;; no input, not yet known to be at EOF: should return
638 (read-char-no-hang stream
)
639 (assert (< (- (get-universal-time) time
) 2)))))
643 (with-test (:name
(open :interrupt
) :skipped-on
:win32
)
649 (setf fifo
(sb-posix:mktemp
"SBCL-fifo.XXXXXXX"))
650 (sb-posix:mkfifo fifo
(logior sb-posix
:s-iwusr sb-posix
:s-irusr
))
651 ;; Try to open it (which hangs), and interrupt ourselves with a timer,
652 ;; continue (this used to result in an error due to open(2) returning with
653 ;; EINTR, then interupt again and unwind.
656 (handler-bind ((timeout (lambda (c)
657 (when (eql 1 (incf to
))
660 (with-open-file (f fifo
:direction
:input
)
669 (ignore-errors (delete-file fifo
))))))
672 (with-test (:name
:overeager-character-buffering
:skipped-on
:win32
)
678 ;; (format t "trying ~A~%" format)
682 (setf fifo
(sb-posix:mktemp
"SBCL-fifo-XXXXXXX"))
683 (sb-posix:mkfifo fifo
(logior sb-posix
:s-iwusr sb-posix
:s-irusr
))
684 ;; KLUDGE: because we have both ends in the same process, we would
685 ;; need to use O_NONBLOCK, but this works too.
687 (run-program "/bin/sh"
689 (format nil
"cat > ~A" (native-namestring fifo
)))
692 :external-format format
))
693 (write-line "foobar" (process-input proc
))
694 (finish-output (process-input proc
))
695 (with-open-file (f fifo
:direction
:input
:external-format format
)
696 (assert (equal "foobar" (read-line f
)))))
699 (close (process-input proc
) :abort t
)
701 (ignore-errors (process-close proc
))
704 (ignore-errors (delete-file fifo
))
706 sb-impl
::*external-formats
*)))
708 (with-test (:name
:bug-657183
:skipped-on
'(not :sb-unicode
))
710 (let ((name (merge-pathnames "stream-impure.temp-test"))
711 (text '(#\GREEK_SMALL_LETTER_LAMDA
712 #\JAPANESE_BANK_SYMBOL
714 #\HEAVY_BLACK_HEART
))
715 (positions '(2 5 6 9))
716 (sb-impl::*default-external-format
* :utf-8
))
719 (with-open-file (f name
:external-format
:default
:direction
:output
720 :if-exists
:supersede
)
721 (assert (eql 0 (file-position f
)))
722 (mapc (lambda (char pos
)
724 (assert (eql pos
(file-position f
))))
727 (with-open-file (f name
:external-format
:default
:direction
:input
)
728 (assert (eql 0 (file-position f
)))
729 (assert (eql (pop text
) (read-char f
)))
730 (assert (eql (file-position f
) 2))
731 (assert (eql (pop text
) (read-char f
)))
732 (assert (eql (file-position f
) 5))
733 (assert (eql (pop text
) (read-char f
)))
734 (assert (eql (file-position f
) 6))
735 (assert (eql (pop text
) (read-char f
)))
736 (assert (eql (file-position f
) 9))
737 (assert (eql (file-length f
) 9))))
738 (ignore-errors (delete-file name
)))))
740 (with-test (:name
:bug-561642
)
741 (let ((p "bug-561642-test.tmp"))
745 :if-exists
:supersede
746 :if-does-not-exist
:create
748 (write-line "FOOBAR" f
))
752 (let ((p0 (file-position f
))
754 (write-char #\newline f
)
757 (write-char #\newline f
)
762 (assert (eql 9 p2
)))))
763 (ignore-errors (delete-file p
)))))
765 (defstruct (mock-fd-stream
766 (:constructor %make-mock-fd-stream
(buffer-chain))
767 (:include sb-impl
::ansi-stream
768 (in #'mock-fd-stream-in
)
769 (n-bin #'mock-fd-stream-n-bin
)
771 (make-array sb-impl
::+ansi-stream-in-buffer-length
+
772 :element-type
'character
))))
775 (defun make-mock-fd-stream (buffer-chain)
776 ;; For notational convenience, #\| becomes #\Newline.
777 (%make-mock-fd-stream
778 (mapcar (lambda (x) (substitute #\Newline
#\| x
)) buffer-chain
)))
780 (defun mock-fd-stream-in (stream eof-err-p eof-val
)
781 (sb-impl::eof-or-lose stream eof-err-p eof-val
))
783 (defun mock-fd-stream-n-bin (stream char-buf start count eof-err-p
)
784 (cond ((mock-fd-stream-buffer-chain stream
)
785 (let* ((chars (pop (mock-fd-stream-buffer-chain stream
)))
786 (n-chars (length chars
)))
787 ;; make sure the mock object is being used as expected.
788 (assert (>= count
(length chars
)))
789 (replace char-buf chars
:start1 start
)
792 (sb-impl::eof-or-lose stream eof-err-p
0))))
794 (with-test (:name
:read-chunk-from-frc-buffer
)
795 (let ((s (make-mock-fd-stream '("zabc" "d" "efgh" "foo|bar" "hi"))))
796 (multiple-value-bind (line eofp
)
797 (sb-impl::ansi-stream-read-line-from-frc-buffer s nil
'woot
)
798 (assert (and (string= line
"zabcdefghfoo") (not eofp
))))
799 (multiple-value-bind (line eofp
)
800 (sb-impl::ansi-stream-read-line-from-frc-buffer s nil
'woot
)
801 (assert (and (string= line
"barhi") eofp
)))
802 (multiple-value-bind (line eofp
)
803 (sb-impl::ansi-stream-read-line-from-frc-buffer s nil
'woot
)
804 (assert (and (eq line
'woot
) eofp
))))
805 (let ((s (make-mock-fd-stream '("zabc" "d" "efgh" "foo*bar" "hi")))
806 (string (make-string 100)))
808 (sb-impl::ansi-stream-read-string-from-frc-buffer string s
10 nil
)))
809 (assert (and (= endpos
28)
810 (string= (subseq string
10 endpos
) "zabcdefghfoo*barhi"))))
812 (sb-impl::ansi-stream-read-string-from-frc-buffer string s
0 nil
)))
813 (assert (= endpos
0)))))