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