Custom binary input ports support 'setvbuf'.
[bpt/guile.git] / test-suite / tests / r6rs-ports.test
CommitLineData
8aa47f26 1;;;; r6rs-ports.test --- R6RS I/O port tests. -*- coding: utf-8; -*-
1ee2c72e 2;;;;
c9d55a7e 3;;;; Copyright (C) 2009, 2010, 2011, 2012, 2014 Free Software Foundation, Inc.
8aa47f26 4;;;; Ludovic Courtès
1ee2c72e
LC
5;;;;
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
53befeb7
NJ
9;;;; version 3 of the License, or (at your option) any later version.
10;;;;
1ee2c72e
LC
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.
53befeb7 15;;;;
1ee2c72e
LC
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
19
20(define-module (test-io-ports)
d4b81637 21 #:use-module (test-suite lib)
b6a66c21 22 #:use-module (test-suite guile-test)
d4b81637
LC
23 #:use-module (srfi srfi-1)
24 #:use-module (srfi srfi-11)
25 #:use-module (rnrs io ports)
b6a66c21 26 #:use-module (rnrs io simple)
b1e76e8f 27 #:use-module (rnrs exceptions)
d4b81637 28 #:use-module (rnrs bytevectors))
1ee2c72e 29
b6a66c21
AR
30(define-syntax pass-if-condition
31 (syntax-rules ()
32 ((_ name predicate body0 body ...)
33 (let ((cookie (list 'cookie)))
34 (pass-if name
35 (eq? cookie (guard (c ((predicate c) cookie))
36 body0 body ...)))))))
37
38(define (test-file)
39 (data-file-name "ports-test.tmp"))
40
41;; A input/output port that swallows all output, and produces just
42;; spaces on input. Reading and writing beyond `failure-position'
43;; produces `system-error' exceptions. Used for testing exception
44;; behavior.
45(define* (make-failing-port #:optional (failure-position 0))
46 (define (maybe-fail index errno)
47 (if (> index failure-position)
48 (scm-error 'system-error
49 'failing-port
50 "I/O beyond failure position" '()
51 (list errno))))
52 (let ((read-index 0)
53 (write-index 0))
54 (define (write-char chr)
55 (set! write-index (+ 1 write-index))
56 (maybe-fail write-index ENOSPC))
57 (make-soft-port
58 (vector write-char
59 (lambda (str) ;; write-string
60 (for-each write-char (string->list str)))
61 (lambda () #t) ;; flush-output
62 (lambda () ;; read-char
63 (set! read-index (+ read-index 1))
64 (maybe-fail read-index EIO)
65 #\space)
66 (lambda () #t)) ;; close-port
67 "rw")))
68
dfc4d56d
AR
69(define (call-with-bytevector-output-port/transcoded transcoder receiver)
70 (call-with-bytevector-output-port
71 (lambda (bv-port)
72 (call-with-port (transcoded-port bv-port transcoder)
73 receiver))))
74
1ee2c72e
LC
75\f
76(with-test-prefix "7.2.5 End-of-File Object"
77
78 (pass-if "eof-object"
79 (and (eqv? (eof-object) (eof-object))
b04f841d
AW
80 (eq? (eof-object) (eof-object))))
81
82 (pass-if "port-eof?"
83 (port-eof? (open-input-string ""))))
1ee2c72e
LC
84
85\f
86(with-test-prefix "7.2.8 Binary Input"
87
88 (pass-if "get-u8"
89 (let ((port (open-input-string "A")))
90 (and (= (char->integer #\A) (get-u8 port))
91 (eof-object? (get-u8 port)))))
92
93 (pass-if "lookahead-u8"
94 (let ((port (open-input-string "A")))
95 (and (= (char->integer #\A) (lookahead-u8 port))
8aa47f26 96 (= (char->integer #\A) (lookahead-u8 port))
1ee2c72e
LC
97 (= (char->integer #\A) (get-u8 port))
98 (eof-object? (get-u8 port)))))
99
8aa47f26
LC
100 (pass-if "lookahead-u8 non-ASCII"
101 (let ((port (with-fluids ((%default-port-encoding "UTF-8"))
102 (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)))))
111
3fa88220
LC
112 (pass-if "lookahead-u8: result is unsigned"
113 ;; Bug #31081.
114 (let ((port (open-bytevector-input-port #vu8(255))))
115 (= (lookahead-u8 port) 255)))
116
1ee2c72e
LC
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 "))))))
123
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"))))))
130
131 (pass-if-exception "get-bytevector-n with closed port"
132 exception:wrong-type-arg
133
134 (let ((port (%make-void-port "r")))
135
136 (close-port port)
137 (get-bytevector-n port 3)))
138
139 (pass-if "get-bytevector-n! [short]"
140 (let* ((port (open-input-string "GNU Guile"))
141 (bv (make-bytevector 4))
142 (read (get-bytevector-n! port bv 0 4)))
143 (and (equal? read 4)
144 (equal? (bytevector->u8-list bv)
145 (map char->integer (string->list "GNU "))))))
146
147 (pass-if "get-bytevector-n! [long]"
148 (let* ((str "GNU Guile")
149 (port (open-input-string str))
150 (bv (make-bytevector 256))
151 (read (get-bytevector-n! port bv 0 256)))
152 (and (equal? read (string-length str))
153 (equal? (map (lambda (i)
154 (bytevector-u8-ref bv i))
155 (iota read))
156 (map char->integer (string->list str))))))
157
158 (pass-if "get-bytevector-some [simple]"
159 (let* ((str "GNU Guile")
160 (port (open-input-string str))
161 (bv (get-bytevector-some port)))
162 (and (bytevector? bv)
163 (equal? (bytevector->u8-list bv)
164 (map char->integer (string->list str))))))
165
1ee2c72e
LC
166 (pass-if "get-bytevector-all"
167 (let* ((str "GNU Guile")
168 (index 0)
169 (port (make-soft-port
170 (vector #f #f #f
171 (lambda ()
172 (if (>= index (string-length str))
173 (eof-object)
174 (let ((c (string-ref str index)))
175 (set! index (+ index 1))
176 c)))
177 (lambda () #t)
178 (let ((cont? #f))
179 (lambda ()
180 ;; Number of readily available octets: falls to
181 ;; zero after 4 octets have been read and then
182 ;; starts again.
183 (let ((a (if cont?
184 (- (string-length str) index)
185 (- 4 (modulo index 5)))))
186 (if (= 0 a) (set! cont? #t))
187 a))))
188 "r"))
189 (bv (get-bytevector-all port)))
190 (and (bytevector? bv)
191 (= index (string-length str))
192 (= (bytevector-length bv) (string-length str))
193 (equal? (bytevector->u8-list bv)
194 (map char->integer (string->list str)))))))
195
196\f
197(define (make-soft-output-port)
198 (let* ((bv (make-bytevector 1024))
199 (read-index 0)
200 (write-index 0)
201 (write-char (lambda (chr)
202 (bytevector-u8-set! bv write-index
203 (char->integer chr))
204 (set! write-index (+ 1 write-index)))))
205 (make-soft-port
206 (vector write-char
207 (lambda (str) ;; write-string
208 (for-each write-char (string->list str)))
209 (lambda () #t) ;; flush-output
210 (lambda () ;; read-char
211 (if (>= read-index (bytevector-length bv))
212 (eof-object)
213 (let ((c (bytevector-u8-ref bv read-index)))
214 (set! read-index (+ read-index 1))
215 (integer->char c))))
216 (lambda () #t)) ;; close-port
217 "rw")))
218
219(with-test-prefix "7.2.11 Binary Output"
220
221 (pass-if "put-u8"
222 (let ((port (make-soft-output-port)))
223 (put-u8 port 77)
224 (equal? (get-u8 port) 77)))
225
bf08e10f
LC
226 ;; Note: The `put-bytevector' tests below require a Latin-1 locale so
227 ;; that the `scm_from_locale_stringn' call in `sf_write' will let all
228 ;; the bytes through, unmodified. This is hacky, but we can't use
229 ;; "custom binary output ports" here because they're only tested
230 ;; later.
231
1ee2c72e 232 (pass-if "put-bytevector [2 args]"
bf08e10f
LC
233 (with-latin1-locale
234 (let ((port (make-soft-output-port))
235 (bv (make-bytevector 256)))
236 (put-bytevector port bv)
237 (equal? (bytevector->u8-list bv)
238 (bytevector->u8-list
239 (get-bytevector-n port (bytevector-length bv)))))))
1ee2c72e
LC
240
241 (pass-if "put-bytevector [3 args]"
bf08e10f
LC
242 (with-latin1-locale
243 (let ((port (make-soft-output-port))
244 (bv (make-bytevector 256))
245 (start 10))
246 (put-bytevector port bv start)
247 (equal? (drop (bytevector->u8-list bv) start)
248 (bytevector->u8-list
249 (get-bytevector-n port (- (bytevector-length bv) start)))))))
1ee2c72e
LC
250
251 (pass-if "put-bytevector [4 args]"
bf08e10f
LC
252 (with-latin1-locale
253 (let ((port (make-soft-output-port))
254 (bv (make-bytevector 256))
255 (start 10)
256 (count 77))
257 (put-bytevector port bv start count)
258 (equal? (take (drop (bytevector->u8-list bv) start) count)
259 (bytevector->u8-list
260 (get-bytevector-n port count))))))
1ee2c72e
LC
261
262 (pass-if-exception "put-bytevector with closed port"
263 exception:wrong-type-arg
264
265 (let* ((bv (make-bytevector 4))
266 (port (%make-void-port "w")))
267
268 (close-port port)
7b041912
LC
269 (put-bytevector port bv)))
270
271 (pass-if "put-bytevector with UTF-16 string port"
272 (let* ((str "hello, world")
273 (bv (string->utf16 str)))
274 (equal? str
275 (with-fluids ((%default-port-encoding "UTF-16BE"))
276 (call-with-output-string
277 (lambda (port)
278 (put-bytevector port bv)))))))
279
ef7e4ba3 280 (pass-if "put-bytevector with wrong-encoding string port"
7b041912
LC
281 (let* ((str "hello, world")
282 (bv (string->utf16 str)))
c62da8f8 283 (catch 'decoding-error
ef7e4ba3 284 (lambda ()
d3a1a74c
LC
285 (with-fluids ((%default-port-encoding "UTF-32")
286 (%default-port-conversion-strategy 'error))
ef7e4ba3
LC
287 (call-with-output-string
288 (lambda (port)
d3a1a74c
LC
289 (put-bytevector port bv)))
290 #f)) ; fail if we reach this point
c62da8f8
LC
291 (lambda (key subr message errno port)
292 (string? (strerror errno)))))))
1ee2c72e
LC
293
294\f
3ae5a02f
AR
295(define (test-input-file-opener open filename)
296 (let ((contents (string->utf8 "GNU λ")))
0687e826
AR
297 ;; Create file
298 (call-with-output-file filename
299 (lambda (port) (put-bytevector port contents)))
300
3ae5a02f 301 (pass-if "opens binary input port with correct contents"
0687e826 302 (with-fluids ((%default-port-encoding "UTF-8"))
3ae5a02f
AR
303 (call-with-port (open-file-input-port filename)
304 (lambda (port)
305 (and (binary-port? port)
306 (input-port? port)
307 (bytevector=? contents (get-bytevector-all port))))))))
0687e826 308
3ae5a02f
AR
309 (delete-file filename))
310
311(with-test-prefix "7.2.7 Input Ports"
312
313 (with-test-prefix "open-file-input-port"
314 (test-input-file-opener open-file-input-port (test-file)))
315
1ee2c72e
LC
316 ;; This section appears here so that it can use the binary input
317 ;; primitives.
318
319 (pass-if "open-bytevector-input-port [1 arg]"
320 (let* ((str "Hello Port!")
321 (bv (u8-list->bytevector (map char->integer
322 (string->list str))))
323 (port (open-bytevector-input-port bv))
324 (read-to-string
325 (lambda (port)
326 (let loop ((chr (read-char port))
327 (result '()))
328 (if (eof-object? chr)
329 (apply string (reverse! result))
330 (loop (read-char port)
331 (cons chr result)))))))
332
333 (equal? (read-to-string port) str)))
334
96128014
LC
335 (pass-if "bytevector-input-port is binary"
336 (with-fluids ((%default-port-encoding "UTF-8"))
337 (binary-port? (open-bytevector-input-port #vu8(1 2 3)))))
338
1ee2c72e
LC
339 (pass-if-exception "bytevector-input-port is read-only"
340 exception:wrong-type-arg
341
342 (let* ((str "Hello Port!")
343 (bv (u8-list->bytevector (map char->integer
344 (string->list str))))
345 (port (open-bytevector-input-port bv #f)))
346
347 (write "hello" port)))
348
349 (pass-if "bytevector input port supports seeking"
350 (let* ((str "Hello Port!")
351 (bv (u8-list->bytevector (map char->integer
352 (string->list str))))
353 (port (open-bytevector-input-port bv #f)))
354
355 (and (port-has-port-position? port)
356 (= 0 (port-position port))
357 (port-has-set-port-position!? port)
358 (begin
359 (set-port-position! port 6)
360 (= 6 (port-position port)))
361 (bytevector=? (get-bytevector-all port)
362 (u8-list->bytevector
363 (map char->integer (string->list "Port!")))))))
364
c89b4529
IP
365 (pass-if "bytevector input port can seek to very end"
366 (let ((empty (open-bytevector-input-port '#vu8()))
367 (not-empty (open-bytevector-input-port '#vu8(1 2 3))))
368 (and (begin (set-port-position! empty (port-position empty))
369 (= 0 (port-position empty)))
370 (begin (get-bytevector-n not-empty 3)
371 (set-port-position! not-empty (port-position not-empty))
372 (= 3 (port-position not-empty))))))
373
1ee2c72e
LC
374 (pass-if-exception "make-custom-binary-input-port [wrong-num-args]"
375 exception:wrong-num-args
376
377 ;; Prior to Guile-R6RS-Libs 0.2, the last 3 arguments were wrongfully
378 ;; optional.
379 (make-custom-binary-input-port "port" (lambda args #t)))
380
381 (pass-if "make-custom-binary-input-port"
382 (let* ((source (make-bytevector 7777))
383 (read! (let ((pos 0)
384 (len (bytevector-length source)))
385 (lambda (bv start count)
386 (let ((amount (min count (- len pos))))
387 (if (> amount 0)
388 (bytevector-copy! source pos
389 bv start amount))
390 (set! pos (+ pos amount))
391 amount))))
392 (port (make-custom-binary-input-port "the port" read!
393 #f #f #f)))
394
96128014
LC
395 (and (binary-port? port)
396 (input-port? port)
397 (bytevector=? (get-bytevector-all port) source))))
1ee2c72e
LC
398
399 (pass-if "custom binary input port does not support `port-position'"
400 (let* ((str "Hello Port!")
401 (source (open-bytevector-input-port
402 (u8-list->bytevector
403 (map char->integer (string->list str)))))
404 (read! (lambda (bv start count)
405 (let ((r (get-bytevector-n! source bv start count)))
406 (if (eof-object? r)
407 0
408 r))))
409 (port (make-custom-binary-input-port "the port" read!
410 #f #f #f)))
411 (not (or (port-has-port-position? port)
412 (port-has-set-port-position!? port)))))
413
6df03222
LC
414 (pass-if-exception "custom binary input port 'read!' returns too much"
415 exception:out-of-range
416 ;; In Guile <= 2.0.9 this would segfault.
417 (let* ((read! (lambda (bv start count)
418 (+ count 4242)))
419 (port (make-custom-binary-input-port "the port" read!
420 #f #f #f)))
421 (get-bytevector-all port)))
422
c9d55a7e
LC
423 (pass-if-equal "custom binary input port supports `port-position', \
424not `set-port-position!'"
425 42
426 (let ((port (make-custom-binary-input-port "the port" (const 0)
427 (const 42) #f #f)))
428 (and (port-has-port-position? port)
429 (not (port-has-set-port-position!? port))
430 (port-position port))))
431
1ee2c72e
LC
432 (pass-if "custom binary input port supports `port-position'"
433 (let* ((str "Hello Port!")
434 (source (open-bytevector-input-port
435 (u8-list->bytevector
436 (map char->integer (string->list str)))))
437 (read! (lambda (bv start count)
438 (let ((r (get-bytevector-n! source bv start count)))
439 (if (eof-object? r)
440 0
441 r))))
442 (get-pos (lambda ()
443 (port-position source)))
444 (set-pos! (lambda (pos)
445 (set-port-position! source pos)))
446 (port (make-custom-binary-input-port "the port" read!
447 get-pos set-pos! #f)))
448
449 (and (port-has-port-position? port)
450 (= 0 (port-position port))
451 (port-has-set-port-position!? port)
452 (begin
453 (set-port-position! port 6)
454 (= 6 (port-position port)))
455 (bytevector=? (get-bytevector-all port)
456 (u8-list->bytevector
457 (map char->integer (string->list "Port!")))))))
458
8ca97482
LC
459 (pass-if-equal "custom binary input port unbuffered & 'port-position'"
460 '(0 2 5 11)
461 ;; Check that the value returned by 'port-position' is correct, and
462 ;; that each 'port-position' call leads one call to the
463 ;; 'get-position' method.
464 (let* ((str "Hello Port!")
465 (output (make-bytevector (string-length str)))
466 (source (with-fluids ((%default-port-encoding "UTF-8"))
467 (open-string-input-port str)))
468 (read! (lambda (bv start count)
469 (let ((r (get-bytevector-n! source bv start count)))
470 (if (eof-object? r)
471 0
472 r))))
473 (pos '())
474 (get-pos (lambda ()
475 (let ((p (port-position source)))
476 (set! pos (cons p pos))
477 p)))
478 (port (make-custom-binary-input-port "the port" read!
479 get-pos #f #f)))
480 (setvbuf port _IONBF)
481 (and (= 0 (port-position port))
482 (begin
483 (get-bytevector-n! port output 0 2)
484 (= 2 (port-position port)))
485 (begin
486 (get-bytevector-n! port output 2 3)
487 (= 5 (port-position port)))
488 (let ((bv (string->utf8 (get-string-all port))))
489 (bytevector-copy! bv 0 output 5 (bytevector-length bv))
490 (= (string-length str) (port-position port)))
491 (bytevector=? output (string->utf8 str))
492 (reverse pos))))
493
494 (pass-if-equal "custom binary input port unbuffered & 'read!' calls"
495 `((2 "He") (3 "llo") (42 " Port!"))
496 (let* ((str "Hello Port!")
497 (source (with-fluids ((%default-port-encoding "UTF-8"))
498 (open-string-input-port str)))
499 (reads '())
500 (read! (lambda (bv start count)
501 (set! reads (cons count reads))
502 (let ((r (get-bytevector-n! source bv start count)))
503 (if (eof-object? r)
504 0
505 r))))
506 (port (make-custom-binary-input-port "the port" read!
507 #f #f #f)))
508
509 (setvbuf port _IONBF)
510 (let ((ret (list (get-bytevector-n port 2)
511 (get-bytevector-n port 3)
512 (get-bytevector-n port 42))))
513 (zip (reverse reads)
514 (map (lambda (obj)
515 (if (bytevector? obj)
516 (utf8->string obj)
517 obj))
518 ret)))))
519
520 (pass-if-equal "custom binary input port, unbuffered then buffered"
521 `((6 "Lorem ") (12 "ipsum dolor ") (777 "sit amet, consectetur…")
522 (777 ,(eof-object)))
523 (let* ((str "Lorem ipsum dolor sit amet, consectetur…")
524 (source (with-fluids ((%default-port-encoding "UTF-8"))
525 (open-string-input-port str)))
526 (reads '())
527 (read! (lambda (bv start count)
528 (set! reads (cons count reads))
529 (let ((r (get-bytevector-n! source bv start count)))
530 (if (eof-object? r)
531 0
532 r))))
533 (port (make-custom-binary-input-port "the port" read!
534 #f #f #f)))
535
536 (setvbuf port _IONBF)
537 (let ((ret (list (get-bytevector-n port 6)
538 (get-bytevector-n port 12)
539 (begin
540 (setvbuf port _IOFBF 777)
541 (get-bytevector-n port 42))
542 (get-bytevector-n port 42))))
543 (zip (reverse reads)
544 (map (lambda (obj)
545 (if (bytevector? obj)
546 (utf8->string obj)
547 obj))
548 ret)))))
549
550 (pass-if-equal "custom binary input port, buffered then unbuffered"
551 `((18
552 42 14 ; scm_c_read tries to fill the 42-byte buffer
553 42)
554 ("Lorem " "ipsum dolor " "sit amet, consectetur bla…" ,(eof-object)))
555 (let* ((str "Lorem ipsum dolor sit amet, consectetur bla…")
556 (source (with-fluids ((%default-port-encoding "UTF-8"))
557 (open-string-input-port str)))
558 (reads '())
559 (read! (lambda (bv start count)
560 (set! reads (cons count reads))
561 (let ((r (get-bytevector-n! source bv start count)))
562 (if (eof-object? r)
563 0
564 r))))
565 (port (make-custom-binary-input-port "the port" read!
566 #f #f #f)))
567
568 (setvbuf port _IOFBF 18)
569 (let ((ret (list (get-bytevector-n port 6)
570 (get-bytevector-n port 12)
571 (begin
572 (setvbuf port _IONBF)
573 (get-bytevector-n port 42))
574 (get-bytevector-n port 42))))
575 (list (reverse reads)
576 (map (lambda (obj)
577 (if (bytevector? obj)
578 (utf8->string obj)
579 obj))
580 ret)))))
581
1ee2c72e
LC
582 (pass-if "custom binary input port `close-proc' is called"
583 (let* ((closed? #f)
584 (read! (lambda (bv start count) 0))
585 (get-pos (lambda () 0))
586 (set-pos! (lambda (pos) #f))
587 (close! (lambda () (set! closed? #t)))
588 (port (make-custom-binary-input-port "the port" read!
589 get-pos set-pos!
590 close!)))
591
592 (close-port port)
4574ec21 593 (gc) ; Test for marking a closed port.
ead04a04
AR
594 closed?))
595
596 (pass-if "standard-input-port is binary"
597 (with-fluids ((%default-port-encoding "UTF-8"))
598 (binary-port? (standard-input-port)))))
1ee2c72e
LC
599
600\f
3ae5a02f
AR
601(define (test-output-file-opener open filename)
602 (with-fluids ((%default-port-encoding "UTF-8"))
603 (pass-if "opens binary output port"
604 (call-with-port (open filename)
605 (lambda (port)
606 (put-bytevector port '#vu8(1 2 3))
607 (and (binary-port? port)
608 (output-port? port))))))
609
610 (pass-if-condition "exception: already-exists"
611 i/o-file-already-exists-error?
612 (open filename))
613
614 (pass-if "no-fail no-truncate"
615 (and
616 (call-with-port (open filename (file-options no-fail no-truncate))
617 (lambda (port)
618 (= 0 (port-position port))))
619 (= 3 (stat:size (stat filename)))))
620
621 (pass-if "no-fail"
622 (and
623 (call-with-port (open filename (file-options no-fail))
624 binary-port?)
625 (= 0 (stat:size (stat filename)))))
b6a66c21 626
3ae5a02f 627 (delete-file filename)
b6a66c21 628
3ae5a02f
AR
629 (pass-if-condition "exception: does-not-exist"
630 i/o-file-does-not-exist-error?
631 (open filename (file-options no-create))))
632
633(with-test-prefix "8.2.10 Output ports"
634
635 (with-test-prefix "open-file-output-port"
636 (test-output-file-opener open-file-output-port (test-file)))
b6a66c21 637
1ee2c72e
LC
638 (pass-if "open-bytevector-output-port"
639 (let-values (((port get-content)
640 (open-bytevector-output-port #f)))
641 (let ((source (make-bytevector 7777)))
642 (put-bytevector port source)
643 (and (bytevector=? (get-content) source)
644 (bytevector=? (get-content) (make-bytevector 0))))))
96128014
LC
645
646 (pass-if "bytevector-output-port is binary"
647 (binary-port? (open-bytevector-output-port)))
648
a653d32a
AR
649 (pass-if "open-bytevector-output-port [extract after close]"
650 (let-values (((port get-content)
651 (open-bytevector-output-port)))
652 (let ((source (make-bytevector 12345 #xFE)))
653 (put-bytevector port source)
654 (close-port port)
655 (bytevector=? (get-content) source))))
1ee2c72e
LC
656
657 (pass-if "open-bytevector-output-port [put-u8]"
658 (let-values (((port get-content)
659 (open-bytevector-output-port)))
660 (put-u8 port 77)
661 (and (bytevector=? (get-content) (make-bytevector 1 77))
662 (bytevector=? (get-content) (make-bytevector 0)))))
663
664 (pass-if "open-bytevector-output-port [display]"
665 (let-values (((port get-content)
666 (open-bytevector-output-port)))
667 (display "hello" port)
668 (and (bytevector=? (get-content) (string->utf8 "hello"))
669 (bytevector=? (get-content) (make-bytevector 0)))))
670
671 (pass-if "bytevector output port supports `port-position'"
672 (let-values (((port get-content)
673 (open-bytevector-output-port)))
674 (let ((source (make-bytevector 7777))
675 (overwrite (make-bytevector 33)))
676 (and (port-has-port-position? port)
677 (port-has-set-port-position!? port)
678 (begin
679 (put-bytevector port source)
680 (= (bytevector-length source)
681 (port-position port)))
682 (begin
683 (set-port-position! port 10)
684 (= 10 (port-position port)))
685 (begin
686 (put-bytevector port overwrite)
687 (bytevector-copy! overwrite 0 source 10
688 (bytevector-length overwrite))
689 (= (port-position port)
690 (+ 10 (bytevector-length overwrite))))
691 (bytevector=? (get-content) source)
692 (bytevector=? (get-content) (make-bytevector 0))))))
693
96128014 694 (pass-if "make-custom-binary-output-port"
1ee2c72e
LC
695 (let ((port (make-custom-binary-output-port "cbop"
696 (lambda (x y z) 0)
697 #f #f #f)))
698 (and (output-port? port)
699 (binary-port? port)
700 (not (port-has-port-position? port))
701 (not (port-has-set-port-position!? port)))))
702
703 (pass-if "make-custom-binary-output-port [partial writes]"
704 (let* ((source (uint-list->bytevector (iota 333)
705 (native-endianness) 2))
706 (sink (make-bytevector (bytevector-length source)))
707 (sink-pos 0)
708 (eof? #f)
709 (write! (lambda (bv start count)
710 (if (= 0 count)
711 (begin
712 (set! eof? #t)
713 0)
714 (let ((u8 (bytevector-u8-ref bv start)))
715 ;; Get one byte at a time.
716 (bytevector-u8-set! sink sink-pos u8)
717 (set! sink-pos (+ 1 sink-pos))
718 1))))
719 (port (make-custom-binary-output-port "cbop" write!
720 #f #f #f)))
721 (put-bytevector port source)
722 (and (= sink-pos (bytevector-length source))
723 (not eof?)
724 (bytevector=? sink source))))
725
726 (pass-if "make-custom-binary-output-port [full writes]"
727 (let* ((source (uint-list->bytevector (iota 333)
728 (native-endianness) 2))
729 (sink (make-bytevector (bytevector-length source)))
730 (sink-pos 0)
731 (eof? #f)
732 (write! (lambda (bv start count)
733 (if (= 0 count)
734 (begin
735 (set! eof? #t)
736 0)
737 (begin
738 (bytevector-copy! bv start
739 sink sink-pos
740 count)
741 (set! sink-pos (+ sink-pos count))
742 count))))
743 (port (make-custom-binary-output-port "cbop" write!
744 #f #f #f)))
745 (put-bytevector port source)
746 (and (= sink-pos (bytevector-length source))
747 (not eof?)
ead04a04
AR
748 (bytevector=? sink source))))
749
750 (pass-if "standard-output-port is binary"
751 (with-fluids ((%default-port-encoding "UTF-8"))
752 (binary-port? (standard-output-port))))
753
754 (pass-if "standard-error-port is binary"
755 (with-fluids ((%default-port-encoding "UTF-8"))
756 (binary-port? (standard-error-port)))))
1ee2c72e 757
d4b81637 758\f
1044537d 759(with-test-prefix "8.2.6 Input and output ports"
d4b81637 760
1044537d 761 (pass-if "transcoded-port [output]"
8aa47f26 762 (let ((s "Hello\nÄÖÜ"))
1044537d
AR
763 (bytevector=?
764 (string->utf8 s)
dfc4d56d
AR
765 (call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec))
766 (lambda (utf8-port)
767 (put-string utf8-port s))))))
d4b81637 768
1044537d 769 (pass-if "transcoded-port [input]"
8aa47f26 770 (let ((s "Hello\nÄÖÜ"))
1044537d
AR
771 (string=?
772 s
773 (get-string-all
774 (transcoded-port (open-bytevector-input-port (string->utf8 s))
775 (make-transcoder (utf-8-codec)))))))
d4b81637 776
1044537d 777 (pass-if "transcoded-port [input line]"
8aa47f26 778 (string=? "ÄÖÜ"
1044537d 779 (get-line (transcoded-port
8aa47f26 780 (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
d4b81637
LC
781 (make-transcoder (utf-8-codec))))))
782
783 (pass-if "transcoded-port [error handling mode = raise]"
784 (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
785 (error-handling-mode raise)))
786 (b (open-bytevector-input-port #vu8(255 2 1)))
787 (tp (transcoded-port b t)))
b1e76e8f
LC
788 (guard (c ((i/o-decoding-error? c)
789 (eq? (i/o-error-port c) tp)))
d3a1a74c
LC
790 (get-line tp)
791 #f))) ; fail if we reach this point
d4b81637
LC
792
793 (pass-if "transcoded-port [error handling mode = replace]"
794 (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
795 (error-handling-mode replace)))
796 (b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117)))
797 (tp (transcoded-port b t)))
eed98cbc
LC
798 (string-suffix? "gnu" (get-line tp))))
799
800 (pass-if "transcoded-port, output [error handling mode = raise]"
801 (let-values (((p get)
802 (open-bytevector-output-port)))
803 (let* ((t (make-transcoder (latin-1-codec) (native-eol-style)
804 (error-handling-mode raise)))
805 (tp (transcoded-port p t)))
806 (guard (c ((i/o-encoding-error? c)
807 (and (eq? (i/o-error-port c) tp)
808 (char=? (i/o-encoding-error-char c) #\λ)
809 (bytevector=? (get) (string->utf8 "The letter ")))))
810 (put-string tp "The letter λ cannot be represented in Latin-1.")
ead04a04
AR
811 #f))))
812
813 (pass-if "port-transcoder [binary port]"
814 (not (port-transcoder (open-bytevector-input-port #vu8()))))
815
816 (pass-if "port-transcoder [transcoded port]"
817 (let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo"))
818 (make-transcoder (utf-8-codec))))
819 (t (port-transcoder p)))
820 (and t
821 (transcoder-codec t)
822 (eq? (native-eol-style)
823 (transcoder-eol-style t))
824 (eq? (error-handling-mode replace)
825 (transcoder-error-handling-mode t))))))
1044537d 826
a6c377f7
AR
827(with-test-prefix "8.2.9 Textual input"
828
829 (pass-if "get-string-n [short]"
830 (let ((port (open-input-string "GNU Guile")))
831 (string=? "GNU " (get-string-n port 4))))
832 (pass-if "get-string-n [long]"
833 (let ((port (open-input-string "GNU Guile")))
834 (string=? "GNU Guile" (get-string-n port 256))))
835 (pass-if "get-string-n [eof]"
836 (let ((port (open-input-string "")))
837 (eof-object? (get-string-n port 4))))
838
839 (pass-if "get-string-n! [short]"
840 (let ((port (open-input-string "GNU Guile"))
841 (s (string-copy "Isn't XXX great?")))
842 (and (= 3 (get-string-n! port s 6 3))
b6a66c21
AR
843 (string=? s "Isn't GNU great?"))))
844
845 (with-test-prefix "read error"
846 (pass-if-condition "get-char" i/o-read-error?
847 (get-char (make-failing-port)))
848 (pass-if-condition "lookahead-char" i/o-read-error?
849 (lookahead-char (make-failing-port)))
850 ;; FIXME: these are not yet exception-correct
851 #|
852 (pass-if-condition "get-string-n" i/o-read-error?
853 (get-string-n (make-failing-port) 5))
854 (pass-if-condition "get-string-n!" i/o-read-error?
855 (get-string-n! (make-failing-port) (make-string 5) 0 5))
856 |#
857 (pass-if-condition "get-string-all" i/o-read-error?
858 (get-string-all (make-failing-port 100)))
859 (pass-if-condition "get-line" i/o-read-error?
860 (get-line (make-failing-port)))
861 (pass-if-condition "get-datum" i/o-read-error?
862 (get-datum (make-failing-port)))))
863
dfc4d56d
AR
864(define (encoding-error-predicate char)
865 (lambda (c)
866 (and (i/o-encoding-error? c)
867 (char=? char (i/o-encoding-error-char c)))))
868
b6a66c21
AR
869(with-test-prefix "8.2.12 Textual Output"
870
871 (with-test-prefix "write error"
872 (pass-if-condition "put-char" i/o-write-error?
873 (put-char (make-failing-port) #\G))
874 (pass-if-condition "put-string" i/o-write-error?
875 (put-string (make-failing-port) "Hello World!"))
876 (pass-if-condition "put-datum" i/o-write-error?
dfc4d56d
AR
877 (put-datum (make-failing-port) '(hello world!))))
878 (with-test-prefix "encoding error"
879 (pass-if-condition "put-char" (encoding-error-predicate #\λ)
880 (call-with-bytevector-output-port/transcoded
881 (make-transcoder (latin-1-codec)
882 (native-eol-style)
883 (error-handling-mode raise))
884 (lambda (port)
885 (put-char port #\λ))))
886 (pass-if-condition "put-string" (encoding-error-predicate #\λ)
887 (call-with-bytevector-output-port/transcoded
888 (make-transcoder (latin-1-codec)
889 (native-eol-style)
890 (error-handling-mode raise))
891 (lambda (port)
892 (put-string port "FooλBar"))))))
b6a66c21
AR
893
894(with-test-prefix "8.3 Simple I/O"
895 (with-test-prefix "read error"
896 (pass-if-condition "read-char" i/o-read-error?
897 (read-char (make-failing-port)))
898 (pass-if-condition "peek-char" i/o-read-error?
899 (peek-char (make-failing-port)))
900 (pass-if-condition "read" i/o-read-error?
901 (read (make-failing-port))))
902 (with-test-prefix "write error"
903 (pass-if-condition "display" i/o-write-error?
904 (display "Hi there!" (make-failing-port)))
905 (pass-if-condition "write" i/o-write-error?
906 (write '(hi there!) (make-failing-port)))
907 (pass-if-condition "write-char" i/o-write-error?
908 (write-char #\G (make-failing-port)))
909 (pass-if-condition "newline" i/o-write-error?
910 (newline (make-failing-port))))
911 (let ((filename (test-file)))
912 ;; ensure the test file exists
913 (call-with-output-file filename
914 (lambda (port) (write "foo" port)))
915 (pass-if "call-with-input-file [port is textual]"
916 (call-with-input-file filename textual-port?))
917 (pass-if-condition "call-with-input-file [exception: not-found]"
918 i/o-file-does-not-exist-error?
919 (call-with-input-file ",this-is-highly-unlikely-to-exist!"
920 values))
921 (pass-if-condition "call-with-output-file [exception: already-exists]"
922 i/o-file-already-exists-error?
923 (call-with-output-file filename
924 values))
925 (delete-file filename)))
a6c377f7 926
3ae5a02f
AR
927(with-test-prefix "8.2.13 Input/output ports"
928 (with-test-prefix "open-file-input/output-port [output]"
929 (test-output-file-opener open-file-input/output-port (test-file)))
930 (with-test-prefix "open-file-input/output-port [input]"
931 (test-input-file-opener open-file-input/output-port (test-file))))
932
1ee2c72e 933;;; Local Variables:
1ee2c72e 934;;; mode: scheme
b1e76e8f 935;;; eval: (put 'guard 'scheme-indent-function 1)
1ee2c72e 936;;; End: