1 ;;;; r6rs-ports.test --- R6RS I/O port tests. -*- coding: utf-8; -*-
3 ;;;; Copyright (C) 2009-2012, 2013-2015 Free Software Foundation, Inc.
6 ;;;; This library is free software; you can redistribute it and/or
7 ;;;; modify it under the terms of the GNU Lesser General Public
8 ;;;; License as published by the Free Software Foundation; either
9 ;;;; version 3 of the License, or (at your option) any later version.
11 ;;;; This library is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;;;; Lesser General Public License for more details.
16 ;;;; You should have received a copy of the GNU Lesser General Public
17 ;;;; License along with this library; if not, write to the Free Software
18 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 (define-module (test-io-ports)
21 #:use-module (test-suite lib)
22 #:use-module (test-suite guile-test)
23 #:use-module (srfi srfi-1)
24 #:use-module (srfi srfi-11)
25 #:use-module (ice-9 match)
26 #:use-module (rnrs io ports)
27 #:use-module (rnrs io simple)
28 #:use-module (rnrs exceptions)
29 #:use-module (rnrs bytevectors))
31 (define-syntax pass-if-condition
33 ((_ name predicate body0 body ...)
34 (let ((cookie (list 'cookie)))
36 (eq? cookie (guard (c ((predicate c) cookie))
40 (data-file-name "ports-test.tmp"))
42 ;; A input/output port that swallows all output, and produces just
43 ;; spaces on input. Reading and writing beyond `failure-position'
44 ;; produces `system-error' exceptions. Used for testing exception
46 (define* (make-failing-port #:optional (failure-position 0))
47 (define (maybe-fail index errno)
48 (if (> index failure-position)
49 (scm-error 'system-error
51 "I/O beyond failure position" '()
55 (define (write-char chr)
56 (set! write-index (+ 1 write-index))
57 (maybe-fail write-index ENOSPC))
60 (lambda (str) ;; write-string
61 (for-each write-char (string->list str)))
62 (lambda () #t) ;; flush-output
63 (lambda () ;; read-char
64 (set! read-index (+ read-index 1))
65 (maybe-fail read-index EIO)
67 (lambda () #t)) ;; close-port
70 (define (call-with-bytevector-output-port/transcoded transcoder receiver)
71 (call-with-bytevector-output-port
73 (call-with-port (transcoded-port bv-port transcoder)
77 (with-test-prefix "7.2.5 End-of-File Object"
80 (and (eqv? (eof-object) (eof-object))
81 (eq? (eof-object) (eof-object))))
84 (port-eof? (open-input-string ""))))
87 (with-test-prefix "7.2.8 Binary Input"
90 (let ((port (open-input-string "A")))
91 (and (= (char->integer #\A) (get-u8 port))
92 (eof-object? (get-u8 port)))))
94 (pass-if "lookahead-u8"
95 (let ((port (open-input-string "A")))
96 (and (= (char->integer #\A) (lookahead-u8 port))
97 (= (char->integer #\A) (lookahead-u8 port))
98 (= (char->integer #\A) (get-u8 port))
99 (eof-object? (get-u8 port)))))
101 (pass-if "lookahead-u8 non-ASCII"
102 (let ((port (open-input-string "λ")))
103 (and (= 206 (lookahead-u8 port))
104 (= 206 (lookahead-u8 port))
105 (= 206 (get-u8 port))
106 (= 187 (lookahead-u8 port))
107 (= 187 (lookahead-u8 port))
108 (= 187 (get-u8 port))
109 (eof-object? (lookahead-u8 port))
110 (eof-object? (get-u8 port)))))
112 (pass-if "lookahead-u8: result is unsigned"
114 (let ((port (open-bytevector-input-port #vu8(255))))
115 (= (lookahead-u8 port) 255)))
117 (pass-if "get-bytevector-n [short]"
118 (let* ((port (open-input-string "GNU Guile"))
119 (bv (get-bytevector-n port 4)))
120 (and (bytevector? bv)
121 (equal? (bytevector->u8-list bv)
122 (map char->integer (string->list "GNU "))))))
124 (pass-if "get-bytevector-n [long]"
125 (let* ((port (open-input-string "GNU Guile"))
126 (bv (get-bytevector-n port 256)))
127 (and (bytevector? bv)
128 (equal? (bytevector->u8-list bv)
129 (map char->integer (string->list "GNU Guile"))))))
131 (pass-if-exception "get-bytevector-n with closed port"
132 exception:wrong-type-arg
134 (let ((port (%make-void-port "r")))
137 (get-bytevector-n port 3)))
139 (let ((expected (make-bytevector 20 (char->integer #\a))))
140 (pass-if-equal "http://bugs.gnu.org/17466"
141 ;; <http://bugs.gnu.org/17466> is about a memory corruption
142 ;; whereas bytevector shrunk in 'get-bytevector-n' would keep
143 ;; referring to the previous (larger) bytevector.
145 (let loop ((count 50))
148 (let ((bv (call-with-input-string "aaaaaaaaaaaaaaaaaaaa"
150 (get-bytevector-n port 4096)))))
151 ;; Cause the 4 KiB bytevector initially created by
152 ;; 'get-bytevector-n' to be reclaimed.
153 (make-bytevector 4096)
155 (if (equal? bv expected)
159 (pass-if "get-bytevector-n! [short]"
160 (let* ((port (open-input-string "GNU Guile"))
161 (bv (make-bytevector 4))
162 (read (get-bytevector-n! port bv 0 4)))
164 (equal? (bytevector->u8-list bv)
165 (map char->integer (string->list "GNU "))))))
167 (pass-if "get-bytevector-n! [long]"
168 (let* ((str "GNU Guile")
169 (port (open-input-string str))
170 (bv (make-bytevector 256))
171 (read (get-bytevector-n! port bv 0 256)))
172 (and (equal? read (string-length str))
173 (equal? (map (lambda (i)
174 (bytevector-u8-ref bv i))
176 (map char->integer (string->list str))))))
178 (pass-if "get-bytevector-some [simple]"
179 (let* ((str "GNU Guile")
180 (port (open-input-string str))
181 (bv (get-bytevector-some port)))
182 (and (bytevector? bv)
183 (equal? (bytevector->u8-list bv)
184 (map char->integer (string->list str))))))
186 (pass-if "get-bytevector-all"
187 (let* ((str "GNU Guile")
189 (port (make-soft-port
192 (if (>= index (string-length str))
194 (let ((c (string-ref str index)))
195 (set! index (+ index 1))
200 ;; Number of readily available octets: falls to
201 ;; zero after 4 octets have been read and then
204 (- (string-length str) index)
205 (- 4 (modulo index 5)))))
206 (if (= 0 a) (set! cont? #t))
209 (bv (get-bytevector-all port)))
210 (and (bytevector? bv)
211 (= index (string-length str))
212 (= (bytevector-length bv) (string-length str))
213 (equal? (bytevector->u8-list bv)
214 (map char->integer (string->list str)))))))
217 (define (make-soft-output-port)
218 (let* ((bv (make-bytevector 1024))
221 (write-char (lambda (chr)
222 (bytevector-u8-set! bv write-index
224 (set! write-index (+ 1 write-index)))))
227 (lambda (str) ;; write-string
228 (for-each write-char (string->list str)))
229 (lambda () #t) ;; flush-output
230 (lambda () ;; read-char
231 (if (>= read-index (bytevector-length bv))
233 (let ((c (bytevector-u8-ref bv read-index)))
234 (set! read-index (+ read-index 1))
236 (lambda () #t)) ;; close-port
239 (with-test-prefix "7.2.11 Binary Output"
242 (let ((port (make-soft-output-port)))
244 (equal? (get-u8 port) 77)))
246 ;; Note: The `put-bytevector' tests below require a Latin-1 locale so
247 ;; that the `scm_from_locale_stringn' call in `sf_write' will let all
248 ;; the bytes through, unmodified. This is hacky, but we can't use
249 ;; "custom binary output ports" here because they're only tested
252 (pass-if "put-bytevector [2 args]"
254 (let ((port (make-soft-output-port))
255 (bv (make-bytevector 256)))
256 (put-bytevector port bv)
257 (equal? (bytevector->u8-list bv)
259 (get-bytevector-n port (bytevector-length bv)))))))
261 (pass-if "put-bytevector [3 args]"
263 (let ((port (make-soft-output-port))
264 (bv (make-bytevector 256))
266 (put-bytevector port bv start)
267 (equal? (drop (bytevector->u8-list bv) start)
269 (get-bytevector-n port (- (bytevector-length bv) start)))))))
271 (pass-if "put-bytevector [4 args]"
273 (let ((port (make-soft-output-port))
274 (bv (make-bytevector 256))
277 (put-bytevector port bv start count)
278 (equal? (take (drop (bytevector->u8-list bv) start) count)
280 (get-bytevector-n port count))))))
282 (pass-if-exception "put-bytevector with closed port"
283 exception:wrong-type-arg
285 (let* ((bv (make-bytevector 4))
286 (port (%make-void-port "w")))
289 (put-bytevector port bv)))
291 (pass-if "put-bytevector with UTF-16 string port"
292 (let* ((str "hello, world")
293 (bv (string->utf16 str)))
295 (call-with-output-string
297 (set-port-encoding! port "UTF-16BE")
298 (put-bytevector port bv))))))
300 (pass-if "put-bytevector with wrong-encoding string port"
301 (let* ((str "hello, world")
302 (bv (string->utf16 str)))
303 (catch 'decoding-error
305 (with-fluids ((%default-port-conversion-strategy 'error))
306 (call-with-output-string
308 (set-port-encoding! port "UTF-32")
309 (put-bytevector port bv)))
310 #f)) ; fail if we reach this point
311 (lambda (key subr message errno port)
312 (string? (strerror errno)))))))
315 (define (test-input-file-opener open filename)
316 (let ((contents (string->utf8 "GNU λ")))
318 (call-with-output-file filename
319 (lambda (port) (put-bytevector port contents)))
321 (pass-if "opens binary input port with correct contents"
322 (with-fluids ((%default-port-encoding "UTF-8"))
323 (call-with-port (open-file-input-port filename)
325 (and (binary-port? port)
327 (bytevector=? contents (get-bytevector-all port))))))))
329 (delete-file filename))
331 (with-test-prefix "7.2.7 Input Ports"
333 (with-test-prefix "open-file-input-port"
334 (test-input-file-opener open-file-input-port (test-file)))
336 ;; This section appears here so that it can use the binary input
339 (pass-if "open-bytevector-input-port [1 arg]"
340 (let* ((str "Hello Port!")
341 (bv (u8-list->bytevector (map char->integer
342 (string->list str))))
343 (port (open-bytevector-input-port bv))
346 (let loop ((chr (read-char port))
348 (if (eof-object? chr)
349 (apply string (reverse! result))
350 (loop (read-char port)
351 (cons chr result)))))))
353 (equal? (read-to-string port) str)))
355 (pass-if "bytevector-input-port is binary"
356 (with-fluids ((%default-port-encoding "UTF-8"))
357 (binary-port? (open-bytevector-input-port #vu8(1 2 3)))))
359 (pass-if-exception "bytevector-input-port is read-only"
360 exception:wrong-type-arg
362 (let* ((str "Hello Port!")
363 (bv (u8-list->bytevector (map char->integer
364 (string->list str))))
365 (port (open-bytevector-input-port bv #f)))
367 (write "hello" port)))
369 (pass-if "bytevector input port supports seeking"
370 (let* ((str "Hello Port!")
371 (bv (u8-list->bytevector (map char->integer
372 (string->list str))))
373 (port (open-bytevector-input-port bv #f)))
375 (and (port-has-port-position? port)
376 (= 0 (port-position port))
377 (port-has-set-port-position!? port)
379 (set-port-position! port 6)
380 (= 6 (port-position port)))
381 (bytevector=? (get-bytevector-all port)
383 (map char->integer (string->list "Port!")))))))
385 (pass-if "bytevector input port can seek to very end"
386 (let ((empty (open-bytevector-input-port '#vu8()))
387 (not-empty (open-bytevector-input-port '#vu8(1 2 3))))
388 (and (begin (set-port-position! empty (port-position empty))
389 (= 0 (port-position empty)))
390 (begin (get-bytevector-n not-empty 3)
391 (set-port-position! not-empty (port-position not-empty))
392 (= 3 (port-position not-empty))))))
394 (pass-if-exception "make-custom-binary-input-port [wrong-num-args]"
395 exception:wrong-num-args
397 ;; Prior to Guile-R6RS-Libs 0.2, the last 3 arguments were wrongfully
399 (make-custom-binary-input-port "port" (lambda args #t)))
401 (pass-if "make-custom-binary-input-port"
402 (let* ((source (make-bytevector 7777))
404 (len (bytevector-length source)))
405 (lambda (bv start count)
406 (let ((amount (min count (- len pos))))
408 (bytevector-copy! source pos
410 (set! pos (+ pos amount))
412 (port (make-custom-binary-input-port "the port" read!
415 (and (binary-port? port)
417 (bytevector=? (get-bytevector-all port) source))))
419 (pass-if "custom binary input port does not support `port-position'"
420 (let* ((str "Hello Port!")
421 (source (open-bytevector-input-port
423 (map char->integer (string->list str)))))
424 (read! (lambda (bv start count)
425 (let ((r (get-bytevector-n! source bv start count)))
429 (port (make-custom-binary-input-port "the port" read!
431 (not (or (port-has-port-position? port)
432 (port-has-set-port-position!? port)))))
434 (pass-if-exception "custom binary input port 'read!' returns too much"
435 exception:out-of-range
436 ;; In Guile <= 2.0.9 this would segfault.
437 (let* ((read! (lambda (bv start count)
439 (port (make-custom-binary-input-port "the port" read!
441 (get-bytevector-all port)))
443 (pass-if-equal "custom binary input port supports `port-position', \
444 not `set-port-position!'"
446 (let ((port (make-custom-binary-input-port "the port" (const 0)
448 (and (port-has-port-position? port)
449 (not (port-has-set-port-position!? port))
450 (port-position port))))
452 (pass-if "custom binary input port supports `port-position'"
453 (let* ((str "Hello Port!")
454 (source (open-bytevector-input-port
456 (map char->integer (string->list str)))))
457 (read! (lambda (bv start count)
458 (let ((r (get-bytevector-n! source bv start count)))
463 (port-position source)))
464 (set-pos! (lambda (pos)
465 (set-port-position! source pos)))
466 (port (make-custom-binary-input-port "the port" read!
467 get-pos set-pos! #f)))
469 (and (port-has-port-position? port)
470 (= 0 (port-position port))
471 (port-has-set-port-position!? port)
473 (set-port-position! port 6)
474 (= 6 (port-position port)))
475 (bytevector=? (get-bytevector-all port)
477 (map char->integer (string->list "Port!")))))))
479 (pass-if-equal "custom binary input port buffered partial reads"
481 ;; Check what happens when READ! returns less than COUNT bytes.
482 (let* ((src (string->utf8 "Hello Port!"))
483 (chunks '(2 4 5)) ; provide 2 bytes, then 4, etc.
485 (read! (lambda (bv start count)
488 (bytevector-copy! src offset bv start count)
490 (set! offset (+ offset count))
494 (port (make-custom-binary-input-port "the port"
496 (get-string-all port)))
498 (pass-if-equal "custom binary input port unbuffered & 'port-position'"
500 ;; Check that the value returned by 'port-position' is correct, and
501 ;; that each 'port-position' call leads one call to the
502 ;; 'get-position' method.
503 (let* ((str "Hello Port!")
504 (output (make-bytevector (string-length str)))
505 (source (with-fluids ((%default-port-encoding "UTF-8"))
506 (open-string-input-port str)))
507 (read! (lambda (bv start count)
508 (let ((r (get-bytevector-n! source bv start count)))
514 (let ((p (port-position source)))
515 (set! pos (cons p pos))
517 (port (make-custom-binary-input-port "the port" read!
519 (setvbuf port _IONBF)
520 (and (= 0 (port-position port))
522 (get-bytevector-n! port output 0 2)
523 (= 2 (port-position port)))
525 (get-bytevector-n! port output 2 3)
526 (= 5 (port-position port)))
527 (let ((bv (string->utf8 (get-string-all port))))
528 (bytevector-copy! bv 0 output 5 (bytevector-length bv))
529 (= (string-length str) (port-position port)))
530 (bytevector=? output (string->utf8 str))
533 (pass-if-equal "custom binary input port unbuffered & 'read!' calls"
534 `((2 "He") (3 "llo") (42 " Port!"))
535 (let* ((str "Hello Port!")
536 (source (with-fluids ((%default-port-encoding "UTF-8"))
537 (open-string-input-port str)))
539 (read! (lambda (bv start count)
540 (set! reads (cons count reads))
541 (let ((r (get-bytevector-n! source bv start count)))
545 (port (make-custom-binary-input-port "the port" read!
548 (setvbuf port _IONBF)
549 (let ((ret (list (get-bytevector-n port 2)
550 (get-bytevector-n port 3)
551 (get-bytevector-n port 42))))
554 (if (bytevector? obj)
559 (pass-if-equal "custom binary input port unbuffered & 'get-string-all'"
560 (make-string 1000 #\a)
561 ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
562 ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
563 (let* ((input (with-fluids ((%default-port-encoding #f))
564 (open-input-string (make-string 1000 #\a))))
565 (read! (lambda (bv index count)
566 (let ((n (get-bytevector-n! input bv index
568 (if (eof-object? n) 0 n))))
569 (port (make-custom-binary-input-port "foo" read!
571 (setvbuf port _IONBF)
572 (get-string-all port)))
574 (pass-if-equal "custom binary input port unbuffered UTF-8 & 'get-string-all'"
575 (make-string 1000 #\λ)
576 ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
577 ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
578 (let* ((input (with-fluids ((%default-port-encoding "UTF-8"))
579 (open-input-string (make-string 1000 #\λ))))
580 (read! (lambda (bv index count)
581 (let ((n (get-bytevector-n! input bv index
583 (if (eof-object? n) 0 n))))
584 (port (make-custom-binary-input-port "foo" read!
586 (setvbuf port _IONBF)
587 (set-port-encoding! port "UTF-8")
588 (get-string-all port)))
590 (pass-if-equal "custom binary input port, unbuffered then buffered"
591 `((6 "Lorem ") (12 "ipsum dolor ") (777 "sit amet, consectetur…")
593 (let* ((str "Lorem ipsum dolor sit amet, consectetur…")
594 (source (with-fluids ((%default-port-encoding "UTF-8"))
595 (open-string-input-port str)))
597 (read! (lambda (bv start count)
598 (set! reads (cons count reads))
599 (let ((r (get-bytevector-n! source bv start count)))
603 (port (make-custom-binary-input-port "the port" read!
606 (setvbuf port _IONBF)
607 (let ((ret (list (get-bytevector-n port 6)
608 (get-bytevector-n port 12)
610 (setvbuf port _IOFBF 777)
611 (get-bytevector-n port 42))
612 (get-bytevector-n port 42))))
615 (if (bytevector? obj)
620 (pass-if-equal "custom binary input port, buffered then unbuffered"
622 42 14 ; scm_c_read tries to fill the 42-byte buffer
624 ("Lorem " "ipsum dolor " "sit amet, consectetur bla…" ,(eof-object)))
625 (let* ((str "Lorem ipsum dolor sit amet, consectetur bla…")
626 (source (with-fluids ((%default-port-encoding "UTF-8"))
627 (open-string-input-port str)))
629 (read! (lambda (bv start count)
630 (set! reads (cons count reads))
631 (let ((r (get-bytevector-n! source bv start count)))
635 (port (make-custom-binary-input-port "the port" read!
638 (setvbuf port _IOFBF 18)
639 (let ((ret (list (get-bytevector-n port 6)
640 (get-bytevector-n port 12)
642 (setvbuf port _IONBF)
643 (get-bytevector-n port 42))
644 (get-bytevector-n port 42))))
645 (list (reverse reads)
647 (if (bytevector? obj)
652 (pass-if "custom binary input port `close-proc' is called"
654 (read! (lambda (bv start count) 0))
655 (get-pos (lambda () 0))
656 (set-pos! (lambda (pos) #f))
657 (close! (lambda () (set! closed? #t)))
658 (port (make-custom-binary-input-port "the port" read!
663 (gc) ; Test for marking a closed port.
666 (pass-if "standard-input-port is binary"
667 (with-fluids ((%default-port-encoding "UTF-8"))
668 (binary-port? (standard-input-port)))))
671 (define (test-output-file-opener open filename)
672 (with-fluids ((%default-port-encoding "UTF-8"))
673 (pass-if "opens binary output port"
674 (call-with-port (open filename)
676 (put-bytevector port '#vu8(1 2 3))
677 (and (binary-port? port)
678 (output-port? port))))))
680 (pass-if-condition "exception: already-exists"
681 i/o-file-already-exists-error?
684 (pass-if "no-fail no-truncate"
686 (call-with-port (open filename (file-options no-fail no-truncate))
688 (= 0 (port-position port))))
689 (= 3 (stat:size (stat filename)))))
693 (call-with-port (open filename (file-options no-fail))
695 (= 0 (stat:size (stat filename)))))
697 (delete-file filename)
699 (pass-if-condition "exception: does-not-exist"
700 i/o-file-does-not-exist-error?
701 (open filename (file-options no-create))))
703 (with-test-prefix "8.2.10 Output ports"
705 (with-test-prefix "open-file-output-port"
706 (test-output-file-opener open-file-output-port (test-file)))
708 (pass-if "open-bytevector-output-port"
709 (let-values (((port get-content)
710 (open-bytevector-output-port #f)))
711 (let ((source (make-bytevector 7777)))
712 (put-bytevector port source)
713 (and (bytevector=? (get-content) source)
714 (bytevector=? (get-content) (make-bytevector 0))))))
716 (pass-if "bytevector-output-port is binary"
717 (binary-port? (open-bytevector-output-port)))
719 (pass-if "open-bytevector-output-port [extract after close]"
720 (let-values (((port get-content)
721 (open-bytevector-output-port)))
722 (let ((source (make-bytevector 12345 #xFE)))
723 (put-bytevector port source)
725 (bytevector=? (get-content) source))))
727 (pass-if "open-bytevector-output-port [put-u8]"
728 (let-values (((port get-content)
729 (open-bytevector-output-port)))
731 (and (bytevector=? (get-content) (make-bytevector 1 77))
732 (bytevector=? (get-content) (make-bytevector 0)))))
734 (pass-if "open-bytevector-output-port [display]"
735 (let-values (((port get-content)
736 (open-bytevector-output-port)))
737 (display "hello" port)
738 (and (bytevector=? (get-content) (string->utf8 "hello"))
739 (bytevector=? (get-content) (make-bytevector 0)))))
741 (pass-if "bytevector output port supports `port-position'"
742 (let-values (((port get-content)
743 (open-bytevector-output-port)))
744 (let ((source (make-bytevector 7777))
745 (overwrite (make-bytevector 33)))
746 (and (port-has-port-position? port)
747 (port-has-set-port-position!? port)
749 (put-bytevector port source)
750 (= (bytevector-length source)
751 (port-position port)))
753 (set-port-position! port 10)
754 (= 10 (port-position port)))
756 (put-bytevector port overwrite)
757 (bytevector-copy! overwrite 0 source 10
758 (bytevector-length overwrite))
759 (= (port-position port)
760 (+ 10 (bytevector-length overwrite))))
761 (bytevector=? (get-content) source)
762 (bytevector=? (get-content) (make-bytevector 0))))))
764 (pass-if "make-custom-binary-output-port"
765 (let ((port (make-custom-binary-output-port "cbop"
768 (and (output-port? port)
770 (not (port-has-port-position? port))
771 (not (port-has-set-port-position!? port)))))
773 (pass-if "make-custom-binary-output-port [partial writes]"
774 (let* ((source (uint-list->bytevector (iota 333)
775 (native-endianness) 2))
776 (sink (make-bytevector (bytevector-length source)))
779 (write! (lambda (bv start count)
784 (let ((u8 (bytevector-u8-ref bv start)))
785 ;; Get one byte at a time.
786 (bytevector-u8-set! sink sink-pos u8)
787 (set! sink-pos (+ 1 sink-pos))
789 (port (make-custom-binary-output-port "cbop" write!
791 (put-bytevector port source)
792 (and (= sink-pos (bytevector-length source))
794 (bytevector=? sink source))))
796 (pass-if "make-custom-binary-output-port [full writes]"
797 (let* ((source (uint-list->bytevector (iota 333)
798 (native-endianness) 2))
799 (sink (make-bytevector (bytevector-length source)))
802 (write! (lambda (bv start count)
808 (bytevector-copy! bv start
811 (set! sink-pos (+ sink-pos count))
813 (port (make-custom-binary-output-port "cbop" write!
815 (put-bytevector port source)
816 (and (= sink-pos (bytevector-length source))
818 (bytevector=? sink source))))
820 (pass-if "standard-output-port is binary"
821 (with-fluids ((%default-port-encoding "UTF-8"))
822 (binary-port? (standard-output-port))))
824 (pass-if "standard-error-port is binary"
825 (with-fluids ((%default-port-encoding "UTF-8"))
826 (binary-port? (standard-error-port)))))
829 (with-test-prefix "8.2.6 Input and output ports"
831 (pass-if "transcoded-port [output]"
832 (let ((s "Hello\nÄÖÜ"))
835 (call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec))
837 (put-string utf8-port s))))))
839 (pass-if "transcoded-port [input]"
840 (let ((s "Hello\nÄÖÜ"))
844 (transcoded-port (open-bytevector-input-port (string->utf8 s))
845 (make-transcoder (utf-8-codec)))))))
847 (pass-if "transcoded-port [input line]"
849 (get-line (transcoded-port
850 (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
851 (make-transcoder (utf-8-codec))))))
853 (pass-if "transcoded-port [error handling mode = raise]"
854 (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
855 (error-handling-mode raise)))
856 (b (open-bytevector-input-port #vu8(255 2 1)))
857 (tp (transcoded-port b t)))
858 (guard (c ((i/o-decoding-error? c)
859 (eq? (i/o-error-port c) tp)))
861 #f))) ; fail if we reach this point
863 (pass-if "transcoded-port [error handling mode = replace]"
864 (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
865 (error-handling-mode replace)))
866 (b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117)))
867 (tp (transcoded-port b t)))
868 (string-suffix? "gnu" (get-line tp))))
870 (pass-if "transcoded-port, output [error handling mode = raise]"
871 (let-values (((p get)
872 (open-bytevector-output-port)))
873 (let* ((t (make-transcoder (latin-1-codec) (native-eol-style)
874 (error-handling-mode raise)))
875 (tp (transcoded-port p t)))
876 (guard (c ((i/o-encoding-error? c)
877 (and (eq? (i/o-error-port c) tp)
878 (char=? (i/o-encoding-error-char c) #\λ)
879 (bytevector=? (get) (string->utf8 "The letter ")))))
880 (put-string tp "The letter λ cannot be represented in Latin-1.")
883 (pass-if "port-transcoder [transcoded port]"
884 (let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo"))
885 (make-transcoder (utf-8-codec))))
886 (t (port-transcoder p)))
889 (eq? (native-eol-style)
890 (transcoder-eol-style t))
891 (eq? (error-handling-mode replace)
892 (transcoder-error-handling-mode t))))))
894 (with-test-prefix "8.2.9 Textual input"
896 (pass-if "get-string-n [short]"
897 (let ((port (open-input-string "GNU Guile")))
898 (string=? "GNU " (get-string-n port 4))))
899 (pass-if "get-string-n [long]"
900 (let ((port (open-input-string "GNU Guile")))
901 (string=? "GNU Guile" (get-string-n port 256))))
902 (pass-if "get-string-n [eof]"
903 (let ((port (open-input-string "")))
904 (eof-object? (get-string-n port 4))))
906 (pass-if "get-string-n! [short]"
907 (let ((port (open-input-string "GNU Guile"))
908 (s (string-copy "Isn't XXX great?")))
909 (and (= 3 (get-string-n! port s 6 3))
910 (string=? s "Isn't GNU great?"))))
912 (with-test-prefix "read error"
913 (pass-if-condition "get-char" i/o-read-error?
914 (get-char (make-failing-port)))
915 (pass-if-condition "lookahead-char" i/o-read-error?
916 (lookahead-char (make-failing-port)))
917 ;; FIXME: these are not yet exception-correct
919 (pass-if-condition "get-string-n" i/o-read-error?
920 (get-string-n (make-failing-port) 5))
921 (pass-if-condition "get-string-n!" i/o-read-error?
922 (get-string-n! (make-failing-port) (make-string 5) 0 5))
924 (pass-if-condition "get-string-all" i/o-read-error?
925 (get-string-all (make-failing-port 100)))
926 (pass-if-condition "get-line" i/o-read-error?
927 (get-line (make-failing-port)))
928 (pass-if-condition "get-datum" i/o-read-error?
929 (get-datum (make-failing-port)))))
931 (define (encoding-error-predicate char)
933 (and (i/o-encoding-error? c)
934 (char=? char (i/o-encoding-error-char c)))))
936 (with-test-prefix "8.2.12 Textual Output"
938 (with-test-prefix "write error"
939 (pass-if-condition "put-char" i/o-write-error?
940 (put-char (make-failing-port) #\G))
941 (pass-if-condition "put-string" i/o-write-error?
942 (put-string (make-failing-port) "Hello World!"))
943 (pass-if-condition "put-datum" i/o-write-error?
944 (put-datum (make-failing-port) '(hello world!))))
945 (with-test-prefix "encoding error"
946 (pass-if-condition "put-char" (encoding-error-predicate #\λ)
947 (call-with-bytevector-output-port/transcoded
948 (make-transcoder (latin-1-codec)
950 (error-handling-mode raise))
952 (put-char port #\λ))))
953 (pass-if-condition "put-string" (encoding-error-predicate #\λ)
954 (call-with-bytevector-output-port/transcoded
955 (make-transcoder (latin-1-codec)
957 (error-handling-mode raise))
959 (put-string port "FooλBar"))))))
961 (with-test-prefix "8.3 Simple I/O"
962 (with-test-prefix "read error"
963 (pass-if-condition "read-char" i/o-read-error?
964 (read-char (make-failing-port)))
965 (pass-if-condition "peek-char" i/o-read-error?
966 (peek-char (make-failing-port)))
967 (pass-if-condition "read" i/o-read-error?
968 (read (make-failing-port))))
969 (with-test-prefix "write error"
970 (pass-if-condition "display" i/o-write-error?
971 (display "Hi there!" (make-failing-port)))
972 (pass-if-condition "write" i/o-write-error?
973 (write '(hi there!) (make-failing-port)))
974 (pass-if-condition "write-char" i/o-write-error?
975 (write-char #\G (make-failing-port)))
976 (pass-if-condition "newline" i/o-write-error?
977 (newline (make-failing-port))))
978 (let ((filename (test-file)))
979 ;; ensure the test file exists
980 (call-with-output-file filename
981 (lambda (port) (write "foo" port)))
982 (pass-if "call-with-input-file [port is textual]"
983 (call-with-input-file filename textual-port?))
984 (pass-if-condition "call-with-input-file [exception: not-found]"
985 i/o-file-does-not-exist-error?
986 (call-with-input-file ",this-is-highly-unlikely-to-exist!"
988 (pass-if-condition "call-with-output-file [exception: already-exists]"
989 i/o-file-already-exists-error?
990 (call-with-output-file filename
992 (delete-file filename)))
994 (with-test-prefix "8.2.13 Input/output ports"
995 (with-test-prefix "open-file-input/output-port [output]"
996 (test-output-file-opener open-file-input/output-port (test-file)))
997 (with-test-prefix "open-file-input/output-port [input]"
998 (test-input-file-opener open-file-input/output-port (test-file))))
1000 ;;; Local Variables:
1002 ;;; eval: (put 'guard 'scheme-indent-function 1)