| 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-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 | |
| 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 | |
| 232 | (pass-if "put-bytevector [2 args]" |
| 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))))))) |
| 240 | |
| 241 | (pass-if "put-bytevector [3 args]" |
| 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))))))) |
| 250 | |
| 251 | (pass-if "put-bytevector [4 args]" |
| 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)))))) |
| 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) |
| 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 | |
| 280 | (pass-if "put-bytevector with wrong-encoding string port" |
| 281 | (let* ((str "hello, world") |
| 282 | (bv (string->utf16 str))) |
| 283 | (catch 'decoding-error |
| 284 | (lambda () |
| 285 | (with-fluids ((%default-port-encoding "UTF-32") |
| 286 | (%default-port-conversion-strategy 'error)) |
| 287 | (call-with-output-string |
| 288 | (lambda (port) |
| 289 | (put-bytevector port bv))) |
| 290 | #f)) ; fail if we reach this point |
| 291 | (lambda (key subr message errno port) |
| 292 | (string? (strerror errno))))))) |
| 293 | |
| 294 | \f |
| 295 | (define (test-input-file-opener open filename) |
| 296 | (let ((contents (string->utf8 "GNU λ"))) |
| 297 | ;; Create file |
| 298 | (call-with-output-file filename |
| 299 | (lambda (port) (put-bytevector port contents))) |
| 300 | |
| 301 | (pass-if "opens binary input port with correct contents" |
| 302 | (with-fluids ((%default-port-encoding "UTF-8")) |
| 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)))))))) |
| 308 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 395 | (and (binary-port? port) |
| 396 | (input-port? port) |
| 397 | (bytevector=? (get-bytevector-all port) source)))) |
| 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 | |
| 414 | (pass-if "custom binary input port supports `port-position'" |
| 415 | (let* ((str "Hello Port!") |
| 416 | (source (open-bytevector-input-port |
| 417 | (u8-list->bytevector |
| 418 | (map char->integer (string->list str))))) |
| 419 | (read! (lambda (bv start count) |
| 420 | (let ((r (get-bytevector-n! source bv start count))) |
| 421 | (if (eof-object? r) |
| 422 | 0 |
| 423 | r)))) |
| 424 | (get-pos (lambda () |
| 425 | (port-position source))) |
| 426 | (set-pos! (lambda (pos) |
| 427 | (set-port-position! source pos))) |
| 428 | (port (make-custom-binary-input-port "the port" read! |
| 429 | get-pos set-pos! #f))) |
| 430 | |
| 431 | (and (port-has-port-position? port) |
| 432 | (= 0 (port-position port)) |
| 433 | (port-has-set-port-position!? port) |
| 434 | (begin |
| 435 | (set-port-position! port 6) |
| 436 | (= 6 (port-position port))) |
| 437 | (bytevector=? (get-bytevector-all port) |
| 438 | (u8-list->bytevector |
| 439 | (map char->integer (string->list "Port!"))))))) |
| 440 | |
| 441 | (pass-if "custom binary input port `close-proc' is called" |
| 442 | (let* ((closed? #f) |
| 443 | (read! (lambda (bv start count) 0)) |
| 444 | (get-pos (lambda () 0)) |
| 445 | (set-pos! (lambda (pos) #f)) |
| 446 | (close! (lambda () (set! closed? #t))) |
| 447 | (port (make-custom-binary-input-port "the port" read! |
| 448 | get-pos set-pos! |
| 449 | close!))) |
| 450 | |
| 451 | (close-port port) |
| 452 | (gc) ; Test for marking a closed port. |
| 453 | closed?)) |
| 454 | |
| 455 | (pass-if "standard-input-port is binary" |
| 456 | (with-fluids ((%default-port-encoding "UTF-8")) |
| 457 | (binary-port? (standard-input-port))))) |
| 458 | |
| 459 | \f |
| 460 | (define (test-output-file-opener open filename) |
| 461 | (with-fluids ((%default-port-encoding "UTF-8")) |
| 462 | (pass-if "opens binary output port" |
| 463 | (call-with-port (open filename) |
| 464 | (lambda (port) |
| 465 | (put-bytevector port '#vu8(1 2 3)) |
| 466 | (and (binary-port? port) |
| 467 | (output-port? port)))))) |
| 468 | |
| 469 | (pass-if-condition "exception: already-exists" |
| 470 | i/o-file-already-exists-error? |
| 471 | (open filename)) |
| 472 | |
| 473 | (pass-if "no-fail no-truncate" |
| 474 | (and |
| 475 | (call-with-port (open filename (file-options no-fail no-truncate)) |
| 476 | (lambda (port) |
| 477 | (= 0 (port-position port)))) |
| 478 | (= 3 (stat:size (stat filename))))) |
| 479 | |
| 480 | (pass-if "no-fail" |
| 481 | (and |
| 482 | (call-with-port (open filename (file-options no-fail)) |
| 483 | binary-port?) |
| 484 | (= 0 (stat:size (stat filename))))) |
| 485 | |
| 486 | (delete-file filename) |
| 487 | |
| 488 | (pass-if-condition "exception: does-not-exist" |
| 489 | i/o-file-does-not-exist-error? |
| 490 | (open filename (file-options no-create)))) |
| 491 | |
| 492 | (with-test-prefix "8.2.10 Output ports" |
| 493 | |
| 494 | (with-test-prefix "open-file-output-port" |
| 495 | (test-output-file-opener open-file-output-port (test-file))) |
| 496 | |
| 497 | (pass-if "open-bytevector-output-port" |
| 498 | (let-values (((port get-content) |
| 499 | (open-bytevector-output-port #f))) |
| 500 | (let ((source (make-bytevector 7777))) |
| 501 | (put-bytevector port source) |
| 502 | (and (bytevector=? (get-content) source) |
| 503 | (bytevector=? (get-content) (make-bytevector 0)))))) |
| 504 | |
| 505 | (pass-if "bytevector-output-port is binary" |
| 506 | (binary-port? (open-bytevector-output-port))) |
| 507 | |
| 508 | (pass-if "open-bytevector-output-port [extract after close]" |
| 509 | (let-values (((port get-content) |
| 510 | (open-bytevector-output-port))) |
| 511 | (let ((source (make-bytevector 12345 #xFE))) |
| 512 | (put-bytevector port source) |
| 513 | (close-port port) |
| 514 | (bytevector=? (get-content) source)))) |
| 515 | |
| 516 | (pass-if "open-bytevector-output-port [put-u8]" |
| 517 | (let-values (((port get-content) |
| 518 | (open-bytevector-output-port))) |
| 519 | (put-u8 port 77) |
| 520 | (and (bytevector=? (get-content) (make-bytevector 1 77)) |
| 521 | (bytevector=? (get-content) (make-bytevector 0))))) |
| 522 | |
| 523 | (pass-if "open-bytevector-output-port [display]" |
| 524 | (let-values (((port get-content) |
| 525 | (open-bytevector-output-port))) |
| 526 | (display "hello" port) |
| 527 | (and (bytevector=? (get-content) (string->utf8 "hello")) |
| 528 | (bytevector=? (get-content) (make-bytevector 0))))) |
| 529 | |
| 530 | (pass-if "bytevector output port supports `port-position'" |
| 531 | (let-values (((port get-content) |
| 532 | (open-bytevector-output-port))) |
| 533 | (let ((source (make-bytevector 7777)) |
| 534 | (overwrite (make-bytevector 33))) |
| 535 | (and (port-has-port-position? port) |
| 536 | (port-has-set-port-position!? port) |
| 537 | (begin |
| 538 | (put-bytevector port source) |
| 539 | (= (bytevector-length source) |
| 540 | (port-position port))) |
| 541 | (begin |
| 542 | (set-port-position! port 10) |
| 543 | (= 10 (port-position port))) |
| 544 | (begin |
| 545 | (put-bytevector port overwrite) |
| 546 | (bytevector-copy! overwrite 0 source 10 |
| 547 | (bytevector-length overwrite)) |
| 548 | (= (port-position port) |
| 549 | (+ 10 (bytevector-length overwrite)))) |
| 550 | (bytevector=? (get-content) source) |
| 551 | (bytevector=? (get-content) (make-bytevector 0)))))) |
| 552 | |
| 553 | (pass-if "make-custom-binary-output-port" |
| 554 | (let ((port (make-custom-binary-output-port "cbop" |
| 555 | (lambda (x y z) 0) |
| 556 | #f #f #f))) |
| 557 | (and (output-port? port) |
| 558 | (binary-port? port) |
| 559 | (not (port-has-port-position? port)) |
| 560 | (not (port-has-set-port-position!? port))))) |
| 561 | |
| 562 | (pass-if "make-custom-binary-output-port [partial writes]" |
| 563 | (let* ((source (uint-list->bytevector (iota 333) |
| 564 | (native-endianness) 2)) |
| 565 | (sink (make-bytevector (bytevector-length source))) |
| 566 | (sink-pos 0) |
| 567 | (eof? #f) |
| 568 | (write! (lambda (bv start count) |
| 569 | (if (= 0 count) |
| 570 | (begin |
| 571 | (set! eof? #t) |
| 572 | 0) |
| 573 | (let ((u8 (bytevector-u8-ref bv start))) |
| 574 | ;; Get one byte at a time. |
| 575 | (bytevector-u8-set! sink sink-pos u8) |
| 576 | (set! sink-pos (+ 1 sink-pos)) |
| 577 | 1)))) |
| 578 | (port (make-custom-binary-output-port "cbop" write! |
| 579 | #f #f #f))) |
| 580 | (put-bytevector port source) |
| 581 | (and (= sink-pos (bytevector-length source)) |
| 582 | (not eof?) |
| 583 | (bytevector=? sink source)))) |
| 584 | |
| 585 | (pass-if "make-custom-binary-output-port [full writes]" |
| 586 | (let* ((source (uint-list->bytevector (iota 333) |
| 587 | (native-endianness) 2)) |
| 588 | (sink (make-bytevector (bytevector-length source))) |
| 589 | (sink-pos 0) |
| 590 | (eof? #f) |
| 591 | (write! (lambda (bv start count) |
| 592 | (if (= 0 count) |
| 593 | (begin |
| 594 | (set! eof? #t) |
| 595 | 0) |
| 596 | (begin |
| 597 | (bytevector-copy! bv start |
| 598 | sink sink-pos |
| 599 | count) |
| 600 | (set! sink-pos (+ sink-pos count)) |
| 601 | count)))) |
| 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 "standard-output-port is binary" |
| 610 | (with-fluids ((%default-port-encoding "UTF-8")) |
| 611 | (binary-port? (standard-output-port)))) |
| 612 | |
| 613 | (pass-if "standard-error-port is binary" |
| 614 | (with-fluids ((%default-port-encoding "UTF-8")) |
| 615 | (binary-port? (standard-error-port))))) |
| 616 | |
| 617 | \f |
| 618 | (with-test-prefix "8.2.6 Input and output ports" |
| 619 | |
| 620 | (pass-if "transcoded-port [output]" |
| 621 | (let ((s "Hello\nÄÖÜ")) |
| 622 | (bytevector=? |
| 623 | (string->utf8 s) |
| 624 | (call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec)) |
| 625 | (lambda (utf8-port) |
| 626 | (put-string utf8-port s)))))) |
| 627 | |
| 628 | (pass-if "transcoded-port [input]" |
| 629 | (let ((s "Hello\nÄÖÜ")) |
| 630 | (string=? |
| 631 | s |
| 632 | (get-string-all |
| 633 | (transcoded-port (open-bytevector-input-port (string->utf8 s)) |
| 634 | (make-transcoder (utf-8-codec))))))) |
| 635 | |
| 636 | (pass-if "transcoded-port [input line]" |
| 637 | (string=? "ÄÖÜ" |
| 638 | (get-line (transcoded-port |
| 639 | (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar")) |
| 640 | (make-transcoder (utf-8-codec)))))) |
| 641 | |
| 642 | (pass-if "transcoded-port [error handling mode = raise]" |
| 643 | (let* ((t (make-transcoder (utf-8-codec) (native-eol-style) |
| 644 | (error-handling-mode raise))) |
| 645 | (b (open-bytevector-input-port #vu8(255 2 1))) |
| 646 | (tp (transcoded-port b t))) |
| 647 | (guard (c ((i/o-decoding-error? c) |
| 648 | (eq? (i/o-error-port c) tp))) |
| 649 | (get-line tp) |
| 650 | #f))) ; fail if we reach this point |
| 651 | |
| 652 | (pass-if "transcoded-port [error handling mode = replace]" |
| 653 | (let* ((t (make-transcoder (utf-8-codec) (native-eol-style) |
| 654 | (error-handling-mode replace))) |
| 655 | (b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117))) |
| 656 | (tp (transcoded-port b t))) |
| 657 | (string-suffix? "gnu" (get-line tp)))) |
| 658 | |
| 659 | (pass-if "transcoded-port, output [error handling mode = raise]" |
| 660 | (let-values (((p get) |
| 661 | (open-bytevector-output-port))) |
| 662 | (let* ((t (make-transcoder (latin-1-codec) (native-eol-style) |
| 663 | (error-handling-mode raise))) |
| 664 | (tp (transcoded-port p t))) |
| 665 | (guard (c ((i/o-encoding-error? c) |
| 666 | (and (eq? (i/o-error-port c) tp) |
| 667 | (char=? (i/o-encoding-error-char c) #\λ) |
| 668 | (bytevector=? (get) (string->utf8 "The letter "))))) |
| 669 | (put-string tp "The letter λ cannot be represented in Latin-1.") |
| 670 | #f)))) |
| 671 | |
| 672 | (pass-if "port-transcoder [binary port]" |
| 673 | (not (port-transcoder (open-bytevector-input-port #vu8())))) |
| 674 | |
| 675 | (pass-if "port-transcoder [transcoded port]" |
| 676 | (let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo")) |
| 677 | (make-transcoder (utf-8-codec)))) |
| 678 | (t (port-transcoder p))) |
| 679 | (and t |
| 680 | (transcoder-codec t) |
| 681 | (eq? (native-eol-style) |
| 682 | (transcoder-eol-style t)) |
| 683 | (eq? (error-handling-mode replace) |
| 684 | (transcoder-error-handling-mode t)))))) |
| 685 | |
| 686 | (with-test-prefix "8.2.9 Textual input" |
| 687 | |
| 688 | (pass-if "get-string-n [short]" |
| 689 | (let ((port (open-input-string "GNU Guile"))) |
| 690 | (string=? "GNU " (get-string-n port 4)))) |
| 691 | (pass-if "get-string-n [long]" |
| 692 | (let ((port (open-input-string "GNU Guile"))) |
| 693 | (string=? "GNU Guile" (get-string-n port 256)))) |
| 694 | (pass-if "get-string-n [eof]" |
| 695 | (let ((port (open-input-string ""))) |
| 696 | (eof-object? (get-string-n port 4)))) |
| 697 | |
| 698 | (pass-if "get-string-n! [short]" |
| 699 | (let ((port (open-input-string "GNU Guile")) |
| 700 | (s (string-copy "Isn't XXX great?"))) |
| 701 | (and (= 3 (get-string-n! port s 6 3)) |
| 702 | (string=? s "Isn't GNU great?")))) |
| 703 | |
| 704 | (with-test-prefix "read error" |
| 705 | (pass-if-condition "get-char" i/o-read-error? |
| 706 | (get-char (make-failing-port))) |
| 707 | (pass-if-condition "lookahead-char" i/o-read-error? |
| 708 | (lookahead-char (make-failing-port))) |
| 709 | ;; FIXME: these are not yet exception-correct |
| 710 | #| |
| 711 | (pass-if-condition "get-string-n" i/o-read-error? |
| 712 | (get-string-n (make-failing-port) 5)) |
| 713 | (pass-if-condition "get-string-n!" i/o-read-error? |
| 714 | (get-string-n! (make-failing-port) (make-string 5) 0 5)) |
| 715 | |# |
| 716 | (pass-if-condition "get-string-all" i/o-read-error? |
| 717 | (get-string-all (make-failing-port 100))) |
| 718 | (pass-if-condition "get-line" i/o-read-error? |
| 719 | (get-line (make-failing-port))) |
| 720 | (pass-if-condition "get-datum" i/o-read-error? |
| 721 | (get-datum (make-failing-port))))) |
| 722 | |
| 723 | (define (encoding-error-predicate char) |
| 724 | (lambda (c) |
| 725 | (and (i/o-encoding-error? c) |
| 726 | (char=? char (i/o-encoding-error-char c))))) |
| 727 | |
| 728 | (with-test-prefix "8.2.12 Textual Output" |
| 729 | |
| 730 | (with-test-prefix "write error" |
| 731 | (pass-if-condition "put-char" i/o-write-error? |
| 732 | (put-char (make-failing-port) #\G)) |
| 733 | (pass-if-condition "put-string" i/o-write-error? |
| 734 | (put-string (make-failing-port) "Hello World!")) |
| 735 | (pass-if-condition "put-datum" i/o-write-error? |
| 736 | (put-datum (make-failing-port) '(hello world!)))) |
| 737 | (with-test-prefix "encoding error" |
| 738 | (pass-if-condition "put-char" (encoding-error-predicate #\λ) |
| 739 | (call-with-bytevector-output-port/transcoded |
| 740 | (make-transcoder (latin-1-codec) |
| 741 | (native-eol-style) |
| 742 | (error-handling-mode raise)) |
| 743 | (lambda (port) |
| 744 | (put-char port #\λ)))) |
| 745 | (pass-if-condition "put-string" (encoding-error-predicate #\λ) |
| 746 | (call-with-bytevector-output-port/transcoded |
| 747 | (make-transcoder (latin-1-codec) |
| 748 | (native-eol-style) |
| 749 | (error-handling-mode raise)) |
| 750 | (lambda (port) |
| 751 | (put-string port "FooλBar")))))) |
| 752 | |
| 753 | (with-test-prefix "8.3 Simple I/O" |
| 754 | (with-test-prefix "read error" |
| 755 | (pass-if-condition "read-char" i/o-read-error? |
| 756 | (read-char (make-failing-port))) |
| 757 | (pass-if-condition "peek-char" i/o-read-error? |
| 758 | (peek-char (make-failing-port))) |
| 759 | (pass-if-condition "read" i/o-read-error? |
| 760 | (read (make-failing-port)))) |
| 761 | (with-test-prefix "write error" |
| 762 | (pass-if-condition "display" i/o-write-error? |
| 763 | (display "Hi there!" (make-failing-port))) |
| 764 | (pass-if-condition "write" i/o-write-error? |
| 765 | (write '(hi there!) (make-failing-port))) |
| 766 | (pass-if-condition "write-char" i/o-write-error? |
| 767 | (write-char #\G (make-failing-port))) |
| 768 | (pass-if-condition "newline" i/o-write-error? |
| 769 | (newline (make-failing-port)))) |
| 770 | (let ((filename (test-file))) |
| 771 | ;; ensure the test file exists |
| 772 | (call-with-output-file filename |
| 773 | (lambda (port) (write "foo" port))) |
| 774 | (pass-if "call-with-input-file [port is textual]" |
| 775 | (call-with-input-file filename textual-port?)) |
| 776 | (pass-if-condition "call-with-input-file [exception: not-found]" |
| 777 | i/o-file-does-not-exist-error? |
| 778 | (call-with-input-file ",this-is-highly-unlikely-to-exist!" |
| 779 | values)) |
| 780 | (pass-if-condition "call-with-output-file [exception: already-exists]" |
| 781 | i/o-file-already-exists-error? |
| 782 | (call-with-output-file filename |
| 783 | values)) |
| 784 | (delete-file filename))) |
| 785 | |
| 786 | (with-test-prefix "8.2.13 Input/output ports" |
| 787 | (with-test-prefix "open-file-input/output-port [output]" |
| 788 | (test-output-file-opener open-file-input/output-port (test-file))) |
| 789 | (with-test-prefix "open-file-input/output-port [input]" |
| 790 | (test-input-file-opener open-file-input/output-port (test-file)))) |
| 791 | |
| 792 | ;;; Local Variables: |
| 793 | ;;; mode: scheme |
| 794 | ;;; eval: (put 'guard 'scheme-indent-function 1) |
| 795 | ;;; End: |