1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 03:40:34 +02:00
guile/test-suite/tests/r6rs-ports.test
Mark H Weaver 856d318a9f Merge branch 'stable-2.0'
Conflicts:
	benchmark-suite/benchmarks/ports.bm
	libguile/async.h
	libguile/bytevectors.c
	libguile/foreign.c
	libguile/gsubr.c
	libguile/srfi-1.c
	libguile/vm-engine.h
	libguile/vm-i-scheme.c
	module/Makefile.am
	module/language/tree-il/analyze.scm
	module/language/tree-il/peval.scm
	module/scripts/compile.scm
	module/scripts/disassemble.scm
	test-suite/tests/asm-to-bytecode.test
	test-suite/tests/peval.test
	test-suite/tests/rdelim.test
2014-09-30 03:50:47 -04:00

973 lines
38 KiB
Text
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;;; r6rs-ports.test --- R6RS I/O port tests. -*- coding: utf-8; -*-
;;;;
;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013,
;;;; 2014 Free Software Foundation, Inc.
;;;; Ludovic Courtès
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
;;;; License as published by the Free Software Foundation; either
;;;; version 3 of the License, or (at your option) any later version.
;;;;
;;;; This library is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;;; Lesser General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Lesser General Public
;;;; License along with this library; if not, write to the Free Software
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
(define-module (test-io-ports)
#:use-module (test-suite lib)
#:use-module (test-suite guile-test)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (ice-9 match)
#:use-module (rnrs io ports)
#:use-module (rnrs io simple)
#:use-module (rnrs exceptions)
#:use-module (rnrs bytevectors))
(define-syntax pass-if-condition
(syntax-rules ()
((_ name predicate body0 body ...)
(let ((cookie (list 'cookie)))
(pass-if name
(eq? cookie (guard (c ((predicate c) cookie))
body0 body ...)))))))
(define (test-file)
(data-file-name "ports-test.tmp"))
;; A input/output port that swallows all output, and produces just
;; spaces on input. Reading and writing beyond `failure-position'
;; produces `system-error' exceptions. Used for testing exception
;; behavior.
(define* (make-failing-port #:optional (failure-position 0))
(define (maybe-fail index errno)
(if (> index failure-position)
(scm-error 'system-error
'failing-port
"I/O beyond failure position" '()
(list errno))))
(let ((read-index 0)
(write-index 0))
(define (write-char chr)
(set! write-index (+ 1 write-index))
(maybe-fail write-index ENOSPC))
(make-soft-port
(vector write-char
(lambda (str) ;; write-string
(for-each write-char (string->list str)))
(lambda () #t) ;; flush-output
(lambda () ;; read-char
(set! read-index (+ read-index 1))
(maybe-fail read-index EIO)
#\space)
(lambda () #t)) ;; close-port
"rw")))
(define (call-with-bytevector-output-port/transcoded transcoder receiver)
(call-with-bytevector-output-port
(lambda (bv-port)
(call-with-port (transcoded-port bv-port transcoder)
receiver))))
(with-test-prefix "7.2.5 End-of-File Object"
(pass-if "eof-object"
(and (eqv? (eof-object) (eof-object))
(eq? (eof-object) (eof-object))))
(pass-if "port-eof?"
(port-eof? (open-input-string ""))))
(with-test-prefix "7.2.8 Binary Input"
(pass-if "get-u8"
(let ((port (open-input-string "A")))
(and (= (char->integer #\A) (get-u8 port))
(eof-object? (get-u8 port)))))
(pass-if "lookahead-u8"
(let ((port (open-input-string "A")))
(and (= (char->integer #\A) (lookahead-u8 port))
(= (char->integer #\A) (lookahead-u8 port))
(= (char->integer #\A) (get-u8 port))
(eof-object? (get-u8 port)))))
(pass-if "lookahead-u8 non-ASCII"
(let ((port (open-input-string "λ")))
(and (= 206 (lookahead-u8 port))
(= 206 (lookahead-u8 port))
(= 206 (get-u8 port))
(= 187 (lookahead-u8 port))
(= 187 (lookahead-u8 port))
(= 187 (get-u8 port))
(eof-object? (lookahead-u8 port))
(eof-object? (get-u8 port)))))
(pass-if "lookahead-u8: result is unsigned"
;; Bug #31081.
(let ((port (open-bytevector-input-port #vu8(255))))
(= (lookahead-u8 port) 255)))
(pass-if "get-bytevector-n [short]"
(let* ((port (open-input-string "GNU Guile"))
(bv (get-bytevector-n port 4)))
(and (bytevector? bv)
(equal? (bytevector->u8-list bv)
(map char->integer (string->list "GNU "))))))
(pass-if "get-bytevector-n [long]"
(let* ((port (open-input-string "GNU Guile"))
(bv (get-bytevector-n port 256)))
(and (bytevector? bv)
(equal? (bytevector->u8-list bv)
(map char->integer (string->list "GNU Guile"))))))
(pass-if-exception "get-bytevector-n with closed port"
exception:wrong-type-arg
(let ((port (%make-void-port "r")))
(close-port port)
(get-bytevector-n port 3)))
(let ((expected (make-bytevector 20 (char->integer #\a))))
(pass-if-equal "http://bugs.gnu.org/17466"
;; <http://bugs.gnu.org/17466> is about a memory corruption
;; whereas bytevector shrunk in 'get-bytevector-n' would keep
;; referring to the previous (larger) bytevector.
expected
(let loop ((count 50))
(if (zero? count)
expected
(let ((bv (call-with-input-string "aaaaaaaaaaaaaaaaaaaa"
(lambda (port)
(get-bytevector-n port 4096)))))
;; Cause the 4 KiB bytevector initially created by
;; 'get-bytevector-n' to be reclaimed.
(make-bytevector 4096)
(if (equal? bv expected)
(loop (- count 1))
bv))))))
(pass-if "get-bytevector-n! [short]"
(let* ((port (open-input-string "GNU Guile"))
(bv (make-bytevector 4))
(read (get-bytevector-n! port bv 0 4)))
(and (equal? read 4)
(equal? (bytevector->u8-list bv)
(map char->integer (string->list "GNU "))))))
(pass-if "get-bytevector-n! [long]"
(let* ((str "GNU Guile")
(port (open-input-string str))
(bv (make-bytevector 256))
(read (get-bytevector-n! port bv 0 256)))
(and (equal? read (string-length str))
(equal? (map (lambda (i)
(bytevector-u8-ref bv i))
(iota read))
(map char->integer (string->list str))))))
(pass-if "get-bytevector-some [simple]"
(let* ((str "GNU Guile")
(port (open-input-string str))
(bv (get-bytevector-some port)))
(and (bytevector? bv)
(equal? (bytevector->u8-list bv)
(map char->integer (string->list str))))))
(pass-if "get-bytevector-all"
(let* ((str "GNU Guile")
(index 0)
(port (make-soft-port
(vector #f #f #f
(lambda ()
(if (>= index (string-length str))
(eof-object)
(let ((c (string-ref str index)))
(set! index (+ index 1))
c)))
(lambda () #t)
(let ((cont? #f))
(lambda ()
;; Number of readily available octets: falls to
;; zero after 4 octets have been read and then
;; starts again.
(let ((a (if cont?
(- (string-length str) index)
(- 4 (modulo index 5)))))
(if (= 0 a) (set! cont? #t))
a))))
"r"))
(bv (get-bytevector-all port)))
(and (bytevector? bv)
(= index (string-length str))
(= (bytevector-length bv) (string-length str))
(equal? (bytevector->u8-list bv)
(map char->integer (string->list str)))))))
(define (make-soft-output-port)
(let* ((bv (make-bytevector 1024))
(read-index 0)
(write-index 0)
(write-char (lambda (chr)
(bytevector-u8-set! bv write-index
(char->integer chr))
(set! write-index (+ 1 write-index)))))
(make-soft-port
(vector write-char
(lambda (str) ;; write-string
(for-each write-char (string->list str)))
(lambda () #t) ;; flush-output
(lambda () ;; read-char
(if (>= read-index (bytevector-length bv))
(eof-object)
(let ((c (bytevector-u8-ref bv read-index)))
(set! read-index (+ read-index 1))
(integer->char c))))
(lambda () #t)) ;; close-port
"rw")))
(with-test-prefix "7.2.11 Binary Output"
(pass-if "put-u8"
(let ((port (make-soft-output-port)))
(put-u8 port 77)
(equal? (get-u8 port) 77)))
;; Note: The `put-bytevector' tests below require a Latin-1 locale so
;; that the `scm_from_locale_stringn' call in `sf_write' will let all
;; the bytes through, unmodified. This is hacky, but we can't use
;; "custom binary output ports" here because they're only tested
;; later.
(pass-if "put-bytevector [2 args]"
(with-latin1-locale
(let ((port (make-soft-output-port))
(bv (make-bytevector 256)))
(put-bytevector port bv)
(equal? (bytevector->u8-list bv)
(bytevector->u8-list
(get-bytevector-n port (bytevector-length bv)))))))
(pass-if "put-bytevector [3 args]"
(with-latin1-locale
(let ((port (make-soft-output-port))
(bv (make-bytevector 256))
(start 10))
(put-bytevector port bv start)
(equal? (drop (bytevector->u8-list bv) start)
(bytevector->u8-list
(get-bytevector-n port (- (bytevector-length bv) start)))))))
(pass-if "put-bytevector [4 args]"
(with-latin1-locale
(let ((port (make-soft-output-port))
(bv (make-bytevector 256))
(start 10)
(count 77))
(put-bytevector port bv start count)
(equal? (take (drop (bytevector->u8-list bv) start) count)
(bytevector->u8-list
(get-bytevector-n port count))))))
(pass-if-exception "put-bytevector with closed port"
exception:wrong-type-arg
(let* ((bv (make-bytevector 4))
(port (%make-void-port "w")))
(close-port port)
(put-bytevector port bv)))
(pass-if "put-bytevector with UTF-16 string port"
(let* ((str "hello, world")
(bv (string->utf16 str)))
(equal? str
(call-with-output-string
(lambda (port)
(set-port-encoding! port "UTF-16BE")
(put-bytevector port bv))))))
(pass-if "put-bytevector with wrong-encoding string port"
(let* ((str "hello, world")
(bv (string->utf16 str)))
(catch 'decoding-error
(lambda ()
(with-fluids ((%default-port-conversion-strategy 'error))
(call-with-output-string
(lambda (port)
(set-port-encoding! port "UTF-32")
(put-bytevector port bv)))
#f)) ; fail if we reach this point
(lambda (key subr message errno port)
(string? (strerror errno)))))))
(define (test-input-file-opener open filename)
(let ((contents (string->utf8 "GNU λ")))
;; Create file
(call-with-output-file filename
(lambda (port) (put-bytevector port contents)))
(pass-if "opens binary input port with correct contents"
(with-fluids ((%default-port-encoding "UTF-8"))
(call-with-port (open-file-input-port filename)
(lambda (port)
(and (binary-port? port)
(input-port? port)
(bytevector=? contents (get-bytevector-all port))))))))
(delete-file filename))
(with-test-prefix "7.2.7 Input Ports"
(with-test-prefix "open-file-input-port"
(test-input-file-opener open-file-input-port (test-file)))
;; This section appears here so that it can use the binary input
;; primitives.
(pass-if "open-bytevector-input-port [1 arg]"
(let* ((str "Hello Port!")
(bv (u8-list->bytevector (map char->integer
(string->list str))))
(port (open-bytevector-input-port bv))
(read-to-string
(lambda (port)
(let loop ((chr (read-char port))
(result '()))
(if (eof-object? chr)
(apply string (reverse! result))
(loop (read-char port)
(cons chr result)))))))
(equal? (read-to-string port) str)))
(pass-if "bytevector-input-port is binary"
(with-fluids ((%default-port-encoding "UTF-8"))
(binary-port? (open-bytevector-input-port #vu8(1 2 3)))))
(pass-if-exception "bytevector-input-port is read-only"
exception:wrong-type-arg
(let* ((str "Hello Port!")
(bv (u8-list->bytevector (map char->integer
(string->list str))))
(port (open-bytevector-input-port bv #f)))
(write "hello" port)))
(pass-if "bytevector input port supports seeking"
(let* ((str "Hello Port!")
(bv (u8-list->bytevector (map char->integer
(string->list str))))
(port (open-bytevector-input-port bv #f)))
(and (port-has-port-position? port)
(= 0 (port-position port))
(port-has-set-port-position!? port)
(begin
(set-port-position! port 6)
(= 6 (port-position port)))
(bytevector=? (get-bytevector-all port)
(u8-list->bytevector
(map char->integer (string->list "Port!")))))))
(pass-if "bytevector input port can seek to very end"
(let ((empty (open-bytevector-input-port '#vu8()))
(not-empty (open-bytevector-input-port '#vu8(1 2 3))))
(and (begin (set-port-position! empty (port-position empty))
(= 0 (port-position empty)))
(begin (get-bytevector-n not-empty 3)
(set-port-position! not-empty (port-position not-empty))
(= 3 (port-position not-empty))))))
(pass-if-exception "make-custom-binary-input-port [wrong-num-args]"
exception:wrong-num-args
;; Prior to Guile-R6RS-Libs 0.2, the last 3 arguments were wrongfully
;; optional.
(make-custom-binary-input-port "port" (lambda args #t)))
(pass-if "make-custom-binary-input-port"
(let* ((source (make-bytevector 7777))
(read! (let ((pos 0)
(len (bytevector-length source)))
(lambda (bv start count)
(let ((amount (min count (- len pos))))
(if (> amount 0)
(bytevector-copy! source pos
bv start amount))
(set! pos (+ pos amount))
amount))))
(port (make-custom-binary-input-port "the port" read!
#f #f #f)))
(and (binary-port? port)
(input-port? port)
(bytevector=? (get-bytevector-all port) source))))
(pass-if "custom binary input port does not support `port-position'"
(let* ((str "Hello Port!")
(source (open-bytevector-input-port
(u8-list->bytevector
(map char->integer (string->list str)))))
(read! (lambda (bv start count)
(let ((r (get-bytevector-n! source bv start count)))
(if (eof-object? r)
0
r))))
(port (make-custom-binary-input-port "the port" read!
#f #f #f)))
(not (or (port-has-port-position? port)
(port-has-set-port-position!? port)))))
(pass-if-exception "custom binary input port 'read!' returns too much"
exception:out-of-range
;; In Guile <= 2.0.9 this would segfault.
(let* ((read! (lambda (bv start count)
(+ count 4242)))
(port (make-custom-binary-input-port "the port" read!
#f #f #f)))
(get-bytevector-all port)))
(pass-if-equal "custom binary input port supports `port-position', \
not `set-port-position!'"
42
(let ((port (make-custom-binary-input-port "the port" (const 0)
(const 42) #f #f)))
(and (port-has-port-position? port)
(not (port-has-set-port-position!? port))
(port-position port))))
(pass-if "custom binary input port supports `port-position'"
(let* ((str "Hello Port!")
(source (open-bytevector-input-port
(u8-list->bytevector
(map char->integer (string->list str)))))
(read! (lambda (bv start count)
(let ((r (get-bytevector-n! source bv start count)))
(if (eof-object? r)
0
r))))
(get-pos (lambda ()
(port-position source)))
(set-pos! (lambda (pos)
(set-port-position! source pos)))
(port (make-custom-binary-input-port "the port" read!
get-pos set-pos! #f)))
(and (port-has-port-position? port)
(= 0 (port-position port))
(port-has-set-port-position!? port)
(begin
(set-port-position! port 6)
(= 6 (port-position port)))
(bytevector=? (get-bytevector-all port)
(u8-list->bytevector
(map char->integer (string->list "Port!")))))))
(pass-if-equal "custom binary input port buffered partial reads"
"Hello Port!"
;; Check what happens when READ! returns less than COUNT bytes.
(let* ((src (string->utf8 "Hello Port!"))
(chunks '(2 4 5)) ; provide 2 bytes, then 4, etc.
(offset 0)
(read! (lambda (bv start count)
(match chunks
((count rest ...)
(bytevector-copy! src offset bv start count)
(set! chunks rest)
(set! offset (+ offset count))
count)
(()
0))))
(port (make-custom-binary-input-port "the port"
read! #f #f #f)))
(get-string-all port)))
(pass-if-equal "custom binary input port unbuffered & 'port-position'"
'(0 2 5 11)
;; Check that the value returned by 'port-position' is correct, and
;; that each 'port-position' call leads one call to the
;; 'get-position' method.
(let* ((str "Hello Port!")
(output (make-bytevector (string-length str)))
(source (with-fluids ((%default-port-encoding "UTF-8"))
(open-string-input-port str)))
(read! (lambda (bv start count)
(let ((r (get-bytevector-n! source bv start count)))
(if (eof-object? r)
0
r))))
(pos '())
(get-pos (lambda ()
(let ((p (port-position source)))
(set! pos (cons p pos))
p)))
(port (make-custom-binary-input-port "the port" read!
get-pos #f #f)))
(setvbuf port _IONBF)
(and (= 0 (port-position port))
(begin
(get-bytevector-n! port output 0 2)
(= 2 (port-position port)))
(begin
(get-bytevector-n! port output 2 3)
(= 5 (port-position port)))
(let ((bv (string->utf8 (get-string-all port))))
(bytevector-copy! bv 0 output 5 (bytevector-length bv))
(= (string-length str) (port-position port)))
(bytevector=? output (string->utf8 str))
(reverse pos))))
(pass-if-equal "custom binary input port unbuffered & 'read!' calls"
`((2 "He") (3 "llo") (42 " Port!"))
(let* ((str "Hello Port!")
(source (with-fluids ((%default-port-encoding "UTF-8"))
(open-string-input-port str)))
(reads '())
(read! (lambda (bv start count)
(set! reads (cons count reads))
(let ((r (get-bytevector-n! source bv start count)))
(if (eof-object? r)
0
r))))
(port (make-custom-binary-input-port "the port" read!
#f #f #f)))
(setvbuf port _IONBF)
(let ((ret (list (get-bytevector-n port 2)
(get-bytevector-n port 3)
(get-bytevector-n port 42))))
(zip (reverse reads)
(map (lambda (obj)
(if (bytevector? obj)
(utf8->string obj)
obj))
ret)))))
(pass-if-equal "custom binary input port, unbuffered then buffered"
`((6 "Lorem ") (12 "ipsum dolor ") (777 "sit amet, consectetur…")
(777 ,(eof-object)))
(let* ((str "Lorem ipsum dolor sit amet, consectetur…")
(source (with-fluids ((%default-port-encoding "UTF-8"))
(open-string-input-port str)))
(reads '())
(read! (lambda (bv start count)
(set! reads (cons count reads))
(let ((r (get-bytevector-n! source bv start count)))
(if (eof-object? r)
0
r))))
(port (make-custom-binary-input-port "the port" read!
#f #f #f)))
(setvbuf port _IONBF)
(let ((ret (list (get-bytevector-n port 6)
(get-bytevector-n port 12)
(begin
(setvbuf port _IOFBF 777)
(get-bytevector-n port 42))
(get-bytevector-n port 42))))
(zip (reverse reads)
(map (lambda (obj)
(if (bytevector? obj)
(utf8->string obj)
obj))
ret)))))
(pass-if-equal "custom binary input port, buffered then unbuffered"
`((18
42 14 ; scm_c_read tries to fill the 42-byte buffer
42)
("Lorem " "ipsum dolor " "sit amet, consectetur bla…" ,(eof-object)))
(let* ((str "Lorem ipsum dolor sit amet, consectetur bla…")
(source (with-fluids ((%default-port-encoding "UTF-8"))
(open-string-input-port str)))
(reads '())
(read! (lambda (bv start count)
(set! reads (cons count reads))
(let ((r (get-bytevector-n! source bv start count)))
(if (eof-object? r)
0
r))))
(port (make-custom-binary-input-port "the port" read!
#f #f #f)))
(setvbuf port _IOFBF 18)
(let ((ret (list (get-bytevector-n port 6)
(get-bytevector-n port 12)
(begin
(setvbuf port _IONBF)
(get-bytevector-n port 42))
(get-bytevector-n port 42))))
(list (reverse reads)
(map (lambda (obj)
(if (bytevector? obj)
(utf8->string obj)
obj))
ret)))))
(pass-if "custom binary input port `close-proc' is called"
(let* ((closed? #f)
(read! (lambda (bv start count) 0))
(get-pos (lambda () 0))
(set-pos! (lambda (pos) #f))
(close! (lambda () (set! closed? #t)))
(port (make-custom-binary-input-port "the port" read!
get-pos set-pos!
close!)))
(close-port port)
(gc) ; Test for marking a closed port.
closed?))
(pass-if "standard-input-port is binary"
(with-fluids ((%default-port-encoding "UTF-8"))
(binary-port? (standard-input-port)))))
(define (test-output-file-opener open filename)
(with-fluids ((%default-port-encoding "UTF-8"))
(pass-if "opens binary output port"
(call-with-port (open filename)
(lambda (port)
(put-bytevector port '#vu8(1 2 3))
(and (binary-port? port)
(output-port? port))))))
(pass-if-condition "exception: already-exists"
i/o-file-already-exists-error?
(open filename))
(pass-if "no-fail no-truncate"
(and
(call-with-port (open filename (file-options no-fail no-truncate))
(lambda (port)
(= 0 (port-position port))))
(= 3 (stat:size (stat filename)))))
(pass-if "no-fail"
(and
(call-with-port (open filename (file-options no-fail))
binary-port?)
(= 0 (stat:size (stat filename)))))
(delete-file filename)
(pass-if-condition "exception: does-not-exist"
i/o-file-does-not-exist-error?
(open filename (file-options no-create))))
(with-test-prefix "8.2.10 Output ports"
(with-test-prefix "open-file-output-port"
(test-output-file-opener open-file-output-port (test-file)))
(pass-if "open-bytevector-output-port"
(let-values (((port get-content)
(open-bytevector-output-port #f)))
(let ((source (make-bytevector 7777)))
(put-bytevector port source)
(and (bytevector=? (get-content) source)
(bytevector=? (get-content) (make-bytevector 0))))))
(pass-if "bytevector-output-port is binary"
(binary-port? (open-bytevector-output-port)))
(pass-if "open-bytevector-output-port [extract after close]"
(let-values (((port get-content)
(open-bytevector-output-port)))
(let ((source (make-bytevector 12345 #xFE)))
(put-bytevector port source)
(close-port port)
(bytevector=? (get-content) source))))
(pass-if "open-bytevector-output-port [put-u8]"
(let-values (((port get-content)
(open-bytevector-output-port)))
(put-u8 port 77)
(and (bytevector=? (get-content) (make-bytevector 1 77))
(bytevector=? (get-content) (make-bytevector 0)))))
(pass-if "open-bytevector-output-port [display]"
(let-values (((port get-content)
(open-bytevector-output-port)))
(display "hello" port)
(and (bytevector=? (get-content) (string->utf8 "hello"))
(bytevector=? (get-content) (make-bytevector 0)))))
(pass-if "bytevector output port supports `port-position'"
(let-values (((port get-content)
(open-bytevector-output-port)))
(let ((source (make-bytevector 7777))
(overwrite (make-bytevector 33)))
(and (port-has-port-position? port)
(port-has-set-port-position!? port)
(begin
(put-bytevector port source)
(= (bytevector-length source)
(port-position port)))
(begin
(set-port-position! port 10)
(= 10 (port-position port)))
(begin
(put-bytevector port overwrite)
(bytevector-copy! overwrite 0 source 10
(bytevector-length overwrite))
(= (port-position port)
(+ 10 (bytevector-length overwrite))))
(bytevector=? (get-content) source)
(bytevector=? (get-content) (make-bytevector 0))))))
(pass-if "make-custom-binary-output-port"
(let ((port (make-custom-binary-output-port "cbop"
(lambda (x y z) 0)
#f #f #f)))
(and (output-port? port)
(binary-port? port)
(not (port-has-port-position? port))
(not (port-has-set-port-position!? port)))))
(pass-if "make-custom-binary-output-port [partial writes]"
(let* ((source (uint-list->bytevector (iota 333)
(native-endianness) 2))
(sink (make-bytevector (bytevector-length source)))
(sink-pos 0)
(eof? #f)
(write! (lambda (bv start count)
(if (= 0 count)
(begin
(set! eof? #t)
0)
(let ((u8 (bytevector-u8-ref bv start)))
;; Get one byte at a time.
(bytevector-u8-set! sink sink-pos u8)
(set! sink-pos (+ 1 sink-pos))
1))))
(port (make-custom-binary-output-port "cbop" write!
#f #f #f)))
(put-bytevector port source)
(and (= sink-pos (bytevector-length source))
(not eof?)
(bytevector=? sink source))))
(pass-if "make-custom-binary-output-port [full writes]"
(let* ((source (uint-list->bytevector (iota 333)
(native-endianness) 2))
(sink (make-bytevector (bytevector-length source)))
(sink-pos 0)
(eof? #f)
(write! (lambda (bv start count)
(if (= 0 count)
(begin
(set! eof? #t)
0)
(begin
(bytevector-copy! bv start
sink sink-pos
count)
(set! sink-pos (+ sink-pos count))
count))))
(port (make-custom-binary-output-port "cbop" write!
#f #f #f)))
(put-bytevector port source)
(and (= sink-pos (bytevector-length source))
(not eof?)
(bytevector=? sink source))))
(pass-if "standard-output-port is binary"
(with-fluids ((%default-port-encoding "UTF-8"))
(binary-port? (standard-output-port))))
(pass-if "standard-error-port is binary"
(with-fluids ((%default-port-encoding "UTF-8"))
(binary-port? (standard-error-port)))))
(with-test-prefix "8.2.6 Input and output ports"
(pass-if "transcoded-port [output]"
(let ((s "Hello\nÄÖÜ"))
(bytevector=?
(string->utf8 s)
(call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec))
(lambda (utf8-port)
(put-string utf8-port s))))))
(pass-if "transcoded-port [input]"
(let ((s "Hello\nÄÖÜ"))
(string=?
s
(get-string-all
(transcoded-port (open-bytevector-input-port (string->utf8 s))
(make-transcoder (utf-8-codec)))))))
(pass-if "transcoded-port [input line]"
(string=? "ÄÖÜ"
(get-line (transcoded-port
(open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
(make-transcoder (utf-8-codec))))))
(pass-if "transcoded-port [error handling mode = raise]"
(let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
(error-handling-mode raise)))
(b (open-bytevector-input-port #vu8(255 2 1)))
(tp (transcoded-port b t)))
(guard (c ((i/o-decoding-error? c)
(eq? (i/o-error-port c) tp)))
(get-line tp)
#f))) ; fail if we reach this point
(pass-if "transcoded-port [error handling mode = replace]"
(let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
(error-handling-mode replace)))
(b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117)))
(tp (transcoded-port b t)))
(string-suffix? "gnu" (get-line tp))))
(pass-if "transcoded-port, output [error handling mode = raise]"
(let-values (((p get)
(open-bytevector-output-port)))
(let* ((t (make-transcoder (latin-1-codec) (native-eol-style)
(error-handling-mode raise)))
(tp (transcoded-port p t)))
(guard (c ((i/o-encoding-error? c)
(and (eq? (i/o-error-port c) tp)
(char=? (i/o-encoding-error-char c) #\λ)
(bytevector=? (get) (string->utf8 "The letter ")))))
(put-string tp "The letter λ cannot be represented in Latin-1.")
#f))))
(pass-if "port-transcoder [transcoded port]"
(let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo"))
(make-transcoder (utf-8-codec))))
(t (port-transcoder p)))
(and t
(transcoder-codec t)
(eq? (native-eol-style)
(transcoder-eol-style t))
(eq? (error-handling-mode replace)
(transcoder-error-handling-mode t))))))
(with-test-prefix "8.2.9 Textual input"
(pass-if "get-string-n [short]"
(let ((port (open-input-string "GNU Guile")))
(string=? "GNU " (get-string-n port 4))))
(pass-if "get-string-n [long]"
(let ((port (open-input-string "GNU Guile")))
(string=? "GNU Guile" (get-string-n port 256))))
(pass-if "get-string-n [eof]"
(let ((port (open-input-string "")))
(eof-object? (get-string-n port 4))))
(pass-if "get-string-n! [short]"
(let ((port (open-input-string "GNU Guile"))
(s (string-copy "Isn't XXX great?")))
(and (= 3 (get-string-n! port s 6 3))
(string=? s "Isn't GNU great?"))))
(with-test-prefix "read error"
(pass-if-condition "get-char" i/o-read-error?
(get-char (make-failing-port)))
(pass-if-condition "lookahead-char" i/o-read-error?
(lookahead-char (make-failing-port)))
;; FIXME: these are not yet exception-correct
#|
(pass-if-condition "get-string-n" i/o-read-error?
(get-string-n (make-failing-port) 5))
(pass-if-condition "get-string-n!" i/o-read-error?
(get-string-n! (make-failing-port) (make-string 5) 0 5))
|#
(pass-if-condition "get-string-all" i/o-read-error?
(get-string-all (make-failing-port 100)))
(pass-if-condition "get-line" i/o-read-error?
(get-line (make-failing-port)))
(pass-if-condition "get-datum" i/o-read-error?
(get-datum (make-failing-port)))))
(define (encoding-error-predicate char)
(lambda (c)
(and (i/o-encoding-error? c)
(char=? char (i/o-encoding-error-char c)))))
(with-test-prefix "8.2.12 Textual Output"
(with-test-prefix "write error"
(pass-if-condition "put-char" i/o-write-error?
(put-char (make-failing-port) #\G))
(pass-if-condition "put-string" i/o-write-error?
(put-string (make-failing-port) "Hello World!"))
(pass-if-condition "put-datum" i/o-write-error?
(put-datum (make-failing-port) '(hello world!))))
(with-test-prefix "encoding error"
(pass-if-condition "put-char" (encoding-error-predicate #\λ)
(call-with-bytevector-output-port/transcoded
(make-transcoder (latin-1-codec)
(native-eol-style)
(error-handling-mode raise))
(lambda (port)
(put-char port #\λ))))
(pass-if-condition "put-string" (encoding-error-predicate #\λ)
(call-with-bytevector-output-port/transcoded
(make-transcoder (latin-1-codec)
(native-eol-style)
(error-handling-mode raise))
(lambda (port)
(put-string port "FooλBar"))))))
(with-test-prefix "8.3 Simple I/O"
(with-test-prefix "read error"
(pass-if-condition "read-char" i/o-read-error?
(read-char (make-failing-port)))
(pass-if-condition "peek-char" i/o-read-error?
(peek-char (make-failing-port)))
(pass-if-condition "read" i/o-read-error?
(read (make-failing-port))))
(with-test-prefix "write error"
(pass-if-condition "display" i/o-write-error?
(display "Hi there!" (make-failing-port)))
(pass-if-condition "write" i/o-write-error?
(write '(hi there!) (make-failing-port)))
(pass-if-condition "write-char" i/o-write-error?
(write-char #\G (make-failing-port)))
(pass-if-condition "newline" i/o-write-error?
(newline (make-failing-port))))
(let ((filename (test-file)))
;; ensure the test file exists
(call-with-output-file filename
(lambda (port) (write "foo" port)))
(pass-if "call-with-input-file [port is textual]"
(call-with-input-file filename textual-port?))
(pass-if-condition "call-with-input-file [exception: not-found]"
i/o-file-does-not-exist-error?
(call-with-input-file ",this-is-highly-unlikely-to-exist!"
values))
(pass-if-condition "call-with-output-file [exception: already-exists]"
i/o-file-already-exists-error?
(call-with-output-file filename
values))
(delete-file filename)))
(with-test-prefix "8.2.13 Input/output ports"
(with-test-prefix "open-file-input/output-port [output]"
(test-output-file-opener open-file-input/output-port (test-file)))
(with-test-prefix "open-file-input/output-port [input]"
(test-input-file-opener open-file-input/output-port (test-file))))
;;; Local Variables:
;;; mode: scheme
;;; eval: (put 'guard 'scheme-indent-function 1)
;;; End: