Merge remote-tracking branch 'origin/stable-2.0'
[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 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 (rnrs io ports)
26 #:use-module (rnrs io simple)
27 #:use-module (rnrs exceptions)
28 #:use-module (rnrs bytevectors))
29
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
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
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))
80 (eq? (eof-object) (eof-object))))
81
82 (pass-if "port-eof?"
83 (port-eof? (open-input-string ""))))
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))
96 (= (char->integer #\A) (lookahead-u8 port))
97 (= (char->integer #\A) (get-u8 port))
98 (eof-object? (get-u8 port)))))
99
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
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
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
166 (pass-if "get-bytevector-some [only-some]"
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 (lambda ()
179 ;; Number of readily available octets: falls to
180 ;; zero after 4 octets have been read.
181 (- 4 (modulo index 5))))
182 "r"))
183 (bv (get-bytevector-some port)))
184 (and (bytevector? bv)
185 (= index 4)
186 (= (bytevector-length bv) index)
187 (equal? (bytevector->u8-list bv)
188 (map char->integer (string->list "GNU "))))))
189
190 (pass-if "get-bytevector-all"
191 (let* ((str "GNU Guile")
192 (index 0)
193 (port (make-soft-port
194 (vector #f #f #f
195 (lambda ()
196 (if (>= index (string-length str))
197 (eof-object)
198 (let ((c (string-ref str index)))
199 (set! index (+ index 1))
200 c)))
201 (lambda () #t)
202 (let ((cont? #f))
203 (lambda ()
204 ;; Number of readily available octets: falls to
205 ;; zero after 4 octets have been read and then
206 ;; starts again.
207 (let ((a (if cont?
208 (- (string-length str) index)
209 (- 4 (modulo index 5)))))
210 (if (= 0 a) (set! cont? #t))
211 a))))
212 "r"))
213 (bv (get-bytevector-all port)))
214 (and (bytevector? bv)
215 (= index (string-length str))
216 (= (bytevector-length bv) (string-length str))
217 (equal? (bytevector->u8-list bv)
218 (map char->integer (string->list str)))))))
219
220 \f
221 (define (make-soft-output-port)
222 (let* ((bv (make-bytevector 1024))
223 (read-index 0)
224 (write-index 0)
225 (write-char (lambda (chr)
226 (bytevector-u8-set! bv write-index
227 (char->integer chr))
228 (set! write-index (+ 1 write-index)))))
229 (make-soft-port
230 (vector write-char
231 (lambda (str) ;; write-string
232 (for-each write-char (string->list str)))
233 (lambda () #t) ;; flush-output
234 (lambda () ;; read-char
235 (if (>= read-index (bytevector-length bv))
236 (eof-object)
237 (let ((c (bytevector-u8-ref bv read-index)))
238 (set! read-index (+ read-index 1))
239 (integer->char c))))
240 (lambda () #t)) ;; close-port
241 "rw")))
242
243 (with-test-prefix "7.2.11 Binary Output"
244
245 (pass-if "put-u8"
246 (let ((port (make-soft-output-port)))
247 (put-u8 port 77)
248 (equal? (get-u8 port) 77)))
249
250 ;; Note: The `put-bytevector' tests below require a Latin-1 locale so
251 ;; that the `scm_from_locale_stringn' call in `sf_write' will let all
252 ;; the bytes through, unmodified. This is hacky, but we can't use
253 ;; "custom binary output ports" here because they're only tested
254 ;; later.
255
256 (pass-if "put-bytevector [2 args]"
257 (with-latin1-locale
258 (let ((port (make-soft-output-port))
259 (bv (make-bytevector 256)))
260 (put-bytevector port bv)
261 (equal? (bytevector->u8-list bv)
262 (bytevector->u8-list
263 (get-bytevector-n port (bytevector-length bv)))))))
264
265 (pass-if "put-bytevector [3 args]"
266 (with-latin1-locale
267 (let ((port (make-soft-output-port))
268 (bv (make-bytevector 256))
269 (start 10))
270 (put-bytevector port bv start)
271 (equal? (drop (bytevector->u8-list bv) start)
272 (bytevector->u8-list
273 (get-bytevector-n port (- (bytevector-length bv) start)))))))
274
275 (pass-if "put-bytevector [4 args]"
276 (with-latin1-locale
277 (let ((port (make-soft-output-port))
278 (bv (make-bytevector 256))
279 (start 10)
280 (count 77))
281 (put-bytevector port bv start count)
282 (equal? (take (drop (bytevector->u8-list bv) start) count)
283 (bytevector->u8-list
284 (get-bytevector-n port count))))))
285
286 (pass-if-exception "put-bytevector with closed port"
287 exception:wrong-type-arg
288
289 (let* ((bv (make-bytevector 4))
290 (port (%make-void-port "w")))
291
292 (close-port port)
293 (put-bytevector port bv)))
294
295 (pass-if "put-bytevector with UTF-16 string port"
296 (let* ((str "hello, world")
297 (bv (string->utf16 str)))
298 (equal? str
299 (with-fluids ((%default-port-encoding "UTF-16BE"))
300 (call-with-output-string
301 (lambda (port)
302 (put-bytevector port bv)))))))
303
304 (pass-if "put-bytevector with wrong-encoding string port"
305 (let* ((str "hello, world")
306 (bv (string->utf16 str)))
307 (catch 'decoding-error
308 (lambda ()
309 (with-fluids ((%default-port-encoding "UTF-32")
310 (%default-port-conversion-strategy 'error))
311 (call-with-output-string
312 (lambda (port)
313 (put-bytevector port bv)))
314 #f)) ; fail if we reach this point
315 (lambda (key subr message errno port)
316 (string? (strerror errno)))))))
317
318 \f
319 (define (test-input-file-opener open filename)
320 (let ((contents (string->utf8 "GNU λ")))
321 ;; Create file
322 (call-with-output-file filename
323 (lambda (port) (put-bytevector port contents)))
324
325 (pass-if "opens binary input port with correct contents"
326 (with-fluids ((%default-port-encoding "UTF-8"))
327 (call-with-port (open-file-input-port filename)
328 (lambda (port)
329 (and (binary-port? port)
330 (input-port? port)
331 (bytevector=? contents (get-bytevector-all port))))))))
332
333 (delete-file filename))
334
335 (with-test-prefix "7.2.7 Input Ports"
336
337 (with-test-prefix "open-file-input-port"
338 (test-input-file-opener open-file-input-port (test-file)))
339
340 ;; This section appears here so that it can use the binary input
341 ;; primitives.
342
343 (pass-if "open-bytevector-input-port [1 arg]"
344 (let* ((str "Hello Port!")
345 (bv (u8-list->bytevector (map char->integer
346 (string->list str))))
347 (port (open-bytevector-input-port bv))
348 (read-to-string
349 (lambda (port)
350 (let loop ((chr (read-char port))
351 (result '()))
352 (if (eof-object? chr)
353 (apply string (reverse! result))
354 (loop (read-char port)
355 (cons chr result)))))))
356
357 (equal? (read-to-string port) str)))
358
359 (pass-if "bytevector-input-port is binary"
360 (with-fluids ((%default-port-encoding "UTF-8"))
361 (binary-port? (open-bytevector-input-port #vu8(1 2 3)))))
362
363 (pass-if-exception "bytevector-input-port is read-only"
364 exception:wrong-type-arg
365
366 (let* ((str "Hello Port!")
367 (bv (u8-list->bytevector (map char->integer
368 (string->list str))))
369 (port (open-bytevector-input-port bv #f)))
370
371 (write "hello" port)))
372
373 (pass-if "bytevector input port supports seeking"
374 (let* ((str "Hello Port!")
375 (bv (u8-list->bytevector (map char->integer
376 (string->list str))))
377 (port (open-bytevector-input-port bv #f)))
378
379 (and (port-has-port-position? port)
380 (= 0 (port-position port))
381 (port-has-set-port-position!? port)
382 (begin
383 (set-port-position! port 6)
384 (= 6 (port-position port)))
385 (bytevector=? (get-bytevector-all port)
386 (u8-list->bytevector
387 (map char->integer (string->list "Port!")))))))
388
389 (pass-if "bytevector input port can seek to very end"
390 (let ((empty (open-bytevector-input-port '#vu8()))
391 (not-empty (open-bytevector-input-port '#vu8(1 2 3))))
392 (and (begin (set-port-position! empty (port-position empty))
393 (= 0 (port-position empty)))
394 (begin (get-bytevector-n not-empty 3)
395 (set-port-position! not-empty (port-position not-empty))
396 (= 3 (port-position not-empty))))))
397
398 (pass-if-exception "make-custom-binary-input-port [wrong-num-args]"
399 exception:wrong-num-args
400
401 ;; Prior to Guile-R6RS-Libs 0.2, the last 3 arguments were wrongfully
402 ;; optional.
403 (make-custom-binary-input-port "port" (lambda args #t)))
404
405 (pass-if "make-custom-binary-input-port"
406 (let* ((source (make-bytevector 7777))
407 (read! (let ((pos 0)
408 (len (bytevector-length source)))
409 (lambda (bv start count)
410 (let ((amount (min count (- len pos))))
411 (if (> amount 0)
412 (bytevector-copy! source pos
413 bv start amount))
414 (set! pos (+ pos amount))
415 amount))))
416 (port (make-custom-binary-input-port "the port" read!
417 #f #f #f)))
418
419 (and (binary-port? port)
420 (input-port? port)
421 (bytevector=? (get-bytevector-all port) source))))
422
423 (pass-if "custom binary input port does not support `port-position'"
424 (let* ((str "Hello Port!")
425 (source (open-bytevector-input-port
426 (u8-list->bytevector
427 (map char->integer (string->list str)))))
428 (read! (lambda (bv start count)
429 (let ((r (get-bytevector-n! source bv start count)))
430 (if (eof-object? r)
431 0
432 r))))
433 (port (make-custom-binary-input-port "the port" read!
434 #f #f #f)))
435 (not (or (port-has-port-position? port)
436 (port-has-set-port-position!? port)))))
437
438 (pass-if "custom binary input port supports `port-position'"
439 (let* ((str "Hello Port!")
440 (source (open-bytevector-input-port
441 (u8-list->bytevector
442 (map char->integer (string->list str)))))
443 (read! (lambda (bv start count)
444 (let ((r (get-bytevector-n! source bv start count)))
445 (if (eof-object? r)
446 0
447 r))))
448 (get-pos (lambda ()
449 (port-position source)))
450 (set-pos! (lambda (pos)
451 (set-port-position! source pos)))
452 (port (make-custom-binary-input-port "the port" read!
453 get-pos set-pos! #f)))
454
455 (and (port-has-port-position? port)
456 (= 0 (port-position port))
457 (port-has-set-port-position!? port)
458 (begin
459 (set-port-position! port 6)
460 (= 6 (port-position port)))
461 (bytevector=? (get-bytevector-all port)
462 (u8-list->bytevector
463 (map char->integer (string->list "Port!")))))))
464
465 (pass-if "custom binary input port `close-proc' is called"
466 (let* ((closed? #f)
467 (read! (lambda (bv start count) 0))
468 (get-pos (lambda () 0))
469 (set-pos! (lambda (pos) #f))
470 (close! (lambda () (set! closed? #t)))
471 (port (make-custom-binary-input-port "the port" read!
472 get-pos set-pos!
473 close!)))
474
475 (close-port port)
476 (gc) ; Test for marking a closed port.
477 closed?))
478
479 (pass-if "standard-input-port is binary"
480 (with-fluids ((%default-port-encoding "UTF-8"))
481 (binary-port? (standard-input-port)))))
482
483 \f
484 (define (test-output-file-opener open filename)
485 (with-fluids ((%default-port-encoding "UTF-8"))
486 (pass-if "opens binary output port"
487 (call-with-port (open filename)
488 (lambda (port)
489 (put-bytevector port '#vu8(1 2 3))
490 (and (binary-port? port)
491 (output-port? port))))))
492
493 (pass-if-condition "exception: already-exists"
494 i/o-file-already-exists-error?
495 (open filename))
496
497 (pass-if "no-fail no-truncate"
498 (and
499 (call-with-port (open filename (file-options no-fail no-truncate))
500 (lambda (port)
501 (= 0 (port-position port))))
502 (= 3 (stat:size (stat filename)))))
503
504 (pass-if "no-fail"
505 (and
506 (call-with-port (open filename (file-options no-fail))
507 binary-port?)
508 (= 0 (stat:size (stat filename)))))
509
510 (delete-file filename)
511
512 (pass-if-condition "exception: does-not-exist"
513 i/o-file-does-not-exist-error?
514 (open filename (file-options no-create))))
515
516 (with-test-prefix "8.2.10 Output ports"
517
518 (with-test-prefix "open-file-output-port"
519 (test-output-file-opener open-file-output-port (test-file)))
520
521 (pass-if "open-bytevector-output-port"
522 (let-values (((port get-content)
523 (open-bytevector-output-port #f)))
524 (let ((source (make-bytevector 7777)))
525 (put-bytevector port source)
526 (and (bytevector=? (get-content) source)
527 (bytevector=? (get-content) (make-bytevector 0))))))
528
529 (pass-if "bytevector-output-port is binary"
530 (binary-port? (open-bytevector-output-port)))
531
532 (pass-if "open-bytevector-output-port [extract after close]"
533 (let-values (((port get-content)
534 (open-bytevector-output-port)))
535 (let ((source (make-bytevector 12345 #xFE)))
536 (put-bytevector port source)
537 (close-port port)
538 (bytevector=? (get-content) source))))
539
540 (pass-if "open-bytevector-output-port [put-u8]"
541 (let-values (((port get-content)
542 (open-bytevector-output-port)))
543 (put-u8 port 77)
544 (and (bytevector=? (get-content) (make-bytevector 1 77))
545 (bytevector=? (get-content) (make-bytevector 0)))))
546
547 (pass-if "open-bytevector-output-port [display]"
548 (let-values (((port get-content)
549 (open-bytevector-output-port)))
550 (display "hello" port)
551 (and (bytevector=? (get-content) (string->utf8 "hello"))
552 (bytevector=? (get-content) (make-bytevector 0)))))
553
554 (pass-if "bytevector output port supports `port-position'"
555 (let-values (((port get-content)
556 (open-bytevector-output-port)))
557 (let ((source (make-bytevector 7777))
558 (overwrite (make-bytevector 33)))
559 (and (port-has-port-position? port)
560 (port-has-set-port-position!? port)
561 (begin
562 (put-bytevector port source)
563 (= (bytevector-length source)
564 (port-position port)))
565 (begin
566 (set-port-position! port 10)
567 (= 10 (port-position port)))
568 (begin
569 (put-bytevector port overwrite)
570 (bytevector-copy! overwrite 0 source 10
571 (bytevector-length overwrite))
572 (= (port-position port)
573 (+ 10 (bytevector-length overwrite))))
574 (bytevector=? (get-content) source)
575 (bytevector=? (get-content) (make-bytevector 0))))))
576
577 (pass-if "make-custom-binary-output-port"
578 (let ((port (make-custom-binary-output-port "cbop"
579 (lambda (x y z) 0)
580 #f #f #f)))
581 (and (output-port? port)
582 (binary-port? port)
583 (not (port-has-port-position? port))
584 (not (port-has-set-port-position!? port)))))
585
586 (pass-if "make-custom-binary-output-port [partial writes]"
587 (let* ((source (uint-list->bytevector (iota 333)
588 (native-endianness) 2))
589 (sink (make-bytevector (bytevector-length source)))
590 (sink-pos 0)
591 (eof? #f)
592 (write! (lambda (bv start count)
593 (if (= 0 count)
594 (begin
595 (set! eof? #t)
596 0)
597 (let ((u8 (bytevector-u8-ref bv start)))
598 ;; Get one byte at a time.
599 (bytevector-u8-set! sink sink-pos u8)
600 (set! sink-pos (+ 1 sink-pos))
601 1))))
602 (port (make-custom-binary-output-port "cbop" write!
603 #f #f #f)))
604 (put-bytevector port source)
605 (and (= sink-pos (bytevector-length source))
606 (not eof?)
607 (bytevector=? sink source))))
608
609 (pass-if "make-custom-binary-output-port [full writes]"
610 (let* ((source (uint-list->bytevector (iota 333)
611 (native-endianness) 2))
612 (sink (make-bytevector (bytevector-length source)))
613 (sink-pos 0)
614 (eof? #f)
615 (write! (lambda (bv start count)
616 (if (= 0 count)
617 (begin
618 (set! eof? #t)
619 0)
620 (begin
621 (bytevector-copy! bv start
622 sink sink-pos
623 count)
624 (set! sink-pos (+ sink-pos count))
625 count))))
626 (port (make-custom-binary-output-port "cbop" write!
627 #f #f #f)))
628 (put-bytevector port source)
629 (and (= sink-pos (bytevector-length source))
630 (not eof?)
631 (bytevector=? sink source))))
632
633 (pass-if "standard-output-port is binary"
634 (with-fluids ((%default-port-encoding "UTF-8"))
635 (binary-port? (standard-output-port))))
636
637 (pass-if "standard-error-port is binary"
638 (with-fluids ((%default-port-encoding "UTF-8"))
639 (binary-port? (standard-error-port)))))
640
641 \f
642 (with-test-prefix "8.2.6 Input and output ports"
643
644 (pass-if "transcoded-port [output]"
645 (let ((s "Hello\nÄÖÜ"))
646 (bytevector=?
647 (string->utf8 s)
648 (call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec))
649 (lambda (utf8-port)
650 (put-string utf8-port s))))))
651
652 (pass-if "transcoded-port [input]"
653 (let ((s "Hello\nÄÖÜ"))
654 (string=?
655 s
656 (get-string-all
657 (transcoded-port (open-bytevector-input-port (string->utf8 s))
658 (make-transcoder (utf-8-codec)))))))
659
660 (pass-if "transcoded-port [input line]"
661 (string=? "ÄÖÜ"
662 (get-line (transcoded-port
663 (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
664 (make-transcoder (utf-8-codec))))))
665
666 (pass-if "transcoded-port [error handling mode = raise]"
667 (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
668 (error-handling-mode raise)))
669 (b (open-bytevector-input-port #vu8(255 2 1)))
670 (tp (transcoded-port b t)))
671 (guard (c ((i/o-decoding-error? c)
672 (eq? (i/o-error-port c) tp)))
673 (get-line tp)
674 #f))) ; fail if we reach this point
675
676 (pass-if "transcoded-port [error handling mode = replace]"
677 (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
678 (error-handling-mode replace)))
679 (b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117)))
680 (tp (transcoded-port b t)))
681 (string-suffix? "gnu" (get-line tp))))
682
683 (pass-if "transcoded-port, output [error handling mode = raise]"
684 (let-values (((p get)
685 (open-bytevector-output-port)))
686 (let* ((t (make-transcoder (latin-1-codec) (native-eol-style)
687 (error-handling-mode raise)))
688 (tp (transcoded-port p t)))
689 (guard (c ((i/o-encoding-error? c)
690 (and (eq? (i/o-error-port c) tp)
691 (char=? (i/o-encoding-error-char c) #\λ)
692 (bytevector=? (get) (string->utf8 "The letter ")))))
693 (put-string tp "The letter λ cannot be represented in Latin-1.")
694 #f))))
695
696 (pass-if "port-transcoder [transcoded port]"
697 (let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo"))
698 (make-transcoder (utf-8-codec))))
699 (t (port-transcoder p)))
700 (and t
701 (transcoder-codec t)
702 (eq? (native-eol-style)
703 (transcoder-eol-style t))
704 (eq? (error-handling-mode replace)
705 (transcoder-error-handling-mode t))))))
706
707 (with-test-prefix "8.2.9 Textual input"
708
709 (pass-if "get-string-n [short]"
710 (let ((port (open-input-string "GNU Guile")))
711 (string=? "GNU " (get-string-n port 4))))
712 (pass-if "get-string-n [long]"
713 (let ((port (open-input-string "GNU Guile")))
714 (string=? "GNU Guile" (get-string-n port 256))))
715 (pass-if "get-string-n [eof]"
716 (let ((port (open-input-string "")))
717 (eof-object? (get-string-n port 4))))
718
719 (pass-if "get-string-n! [short]"
720 (let ((port (open-input-string "GNU Guile"))
721 (s (string-copy "Isn't XXX great?")))
722 (and (= 3 (get-string-n! port s 6 3))
723 (string=? s "Isn't GNU great?"))))
724
725 (with-test-prefix "read error"
726 (pass-if-condition "get-char" i/o-read-error?
727 (get-char (make-failing-port)))
728 (pass-if-condition "lookahead-char" i/o-read-error?
729 (lookahead-char (make-failing-port)))
730 ;; FIXME: these are not yet exception-correct
731 #|
732 (pass-if-condition "get-string-n" i/o-read-error?
733 (get-string-n (make-failing-port) 5))
734 (pass-if-condition "get-string-n!" i/o-read-error?
735 (get-string-n! (make-failing-port) (make-string 5) 0 5))
736 |#
737 (pass-if-condition "get-string-all" i/o-read-error?
738 (get-string-all (make-failing-port 100)))
739 (pass-if-condition "get-line" i/o-read-error?
740 (get-line (make-failing-port)))
741 (pass-if-condition "get-datum" i/o-read-error?
742 (get-datum (make-failing-port)))))
743
744 (define (encoding-error-predicate char)
745 (lambda (c)
746 (and (i/o-encoding-error? c)
747 (char=? char (i/o-encoding-error-char c)))))
748
749 (with-test-prefix "8.2.12 Textual Output"
750
751 (with-test-prefix "write error"
752 (pass-if-condition "put-char" i/o-write-error?
753 (put-char (make-failing-port) #\G))
754 (pass-if-condition "put-string" i/o-write-error?
755 (put-string (make-failing-port) "Hello World!"))
756 (pass-if-condition "put-datum" i/o-write-error?
757 (put-datum (make-failing-port) '(hello world!))))
758 (with-test-prefix "encoding error"
759 (pass-if-condition "put-char" (encoding-error-predicate #\λ)
760 (call-with-bytevector-output-port/transcoded
761 (make-transcoder (latin-1-codec)
762 (native-eol-style)
763 (error-handling-mode raise))
764 (lambda (port)
765 (put-char port #\λ))))
766 (pass-if-condition "put-string" (encoding-error-predicate #\λ)
767 (call-with-bytevector-output-port/transcoded
768 (make-transcoder (latin-1-codec)
769 (native-eol-style)
770 (error-handling-mode raise))
771 (lambda (port)
772 (put-string port "FooλBar"))))))
773
774 (with-test-prefix "8.3 Simple I/O"
775 (with-test-prefix "read error"
776 (pass-if-condition "read-char" i/o-read-error?
777 (read-char (make-failing-port)))
778 (pass-if-condition "peek-char" i/o-read-error?
779 (peek-char (make-failing-port)))
780 (pass-if-condition "read" i/o-read-error?
781 (read (make-failing-port))))
782 (with-test-prefix "write error"
783 (pass-if-condition "display" i/o-write-error?
784 (display "Hi there!" (make-failing-port)))
785 (pass-if-condition "write" i/o-write-error?
786 (write '(hi there!) (make-failing-port)))
787 (pass-if-condition "write-char" i/o-write-error?
788 (write-char #\G (make-failing-port)))
789 (pass-if-condition "newline" i/o-write-error?
790 (newline (make-failing-port))))
791 (let ((filename (test-file)))
792 ;; ensure the test file exists
793 (call-with-output-file filename
794 (lambda (port) (write "foo" port)))
795 (pass-if "call-with-input-file [port is textual]"
796 (call-with-input-file filename textual-port?))
797 (pass-if-condition "call-with-input-file [exception: not-found]"
798 i/o-file-does-not-exist-error?
799 (call-with-input-file ",this-is-highly-unlikely-to-exist!"
800 values))
801 (pass-if-condition "call-with-output-file [exception: already-exists]"
802 i/o-file-already-exists-error?
803 (call-with-output-file filename
804 values))
805 (delete-file filename)))
806
807 (with-test-prefix "8.2.13 Input/output ports"
808 (with-test-prefix "open-file-input/output-port [output]"
809 (test-output-file-opener open-file-input/output-port (test-file)))
810 (with-test-prefix "open-file-input/output-port [input]"
811 (test-input-file-opener open-file-input/output-port (test-file))))
812
813 ;;; Local Variables:
814 ;;; mode: scheme
815 ;;; eval: (put 'guard 'scheme-indent-function 1)
816 ;;; End: