Merge commit '9b5da400dde6e6bc8fd0e318e7ca1feffa5870db'
[bpt/guile.git] / test-suite / tests / ports.test
index 5d3c213..bad4118 100644 (file)
@@ -2,7 +2,7 @@
 ;;;; Jim Blandy <jimb@red-bean.com> --- May 1999
 ;;;;
 ;;;;   Copyright (C) 1999, 2001, 2004, 2006, 2007, 2009, 2010,
-;;;;      2011, 2012, 2013 Free Software Foundation, Inc.
+;;;;      2011, 2012, 2013, 2014 Free Software Foundation, Inc.
 ;;;;
 ;;;; This library is free software; you can redistribute it and/or
 ;;;; modify it under the terms of the GNU Lesser General Public
   #:use-module (ice-9 popen)
   #:use-module (ice-9 rdelim)
   #:use-module (rnrs bytevectors)
-  #:use-module ((rnrs io ports) #:select (open-bytevector-input-port
-                                          open-bytevector-output-port)))
+  #:use-module ((ice-9 binary-ports) #:select (open-bytevector-input-port
+                                               open-bytevector-output-port
+                                               put-bytevector
+                                               get-bytevector-n
+                                               get-bytevector-all
+                                               unget-bytevector)))
 
 (define (display-line . args)
   (for-each display args)
                    (delete-file filename)
                    (string=? line2 binary-test-string)))))
 
-;; open-file ignores file coding declaration
-(pass-if "file: open-file ignores coding declarations"
+;; open-file ignores file coding declaration by default
+(pass-if "file: open-file ignores coding declaration by default"
   (with-fluids ((%default-port-encoding "UTF-8"))
                (let* ((filename (test-file))
                       (port (open-output-file filename))
                    (delete-file filename)
                    (string=? line2 test-string)))))
 
+;; open-input-file with guess-encoding honors coding declaration
+(pass-if "file: open-input-file with guess-encoding honors coding declaration"
+  (with-fluids ((%default-port-encoding "UTF-8"))
+               (let* ((filename (test-file))
+                      (port (open-output-file filename))
+                      (test-string "€100"))
+                 (set-port-encoding! port "iso-8859-15")
+                 (write-line ";; coding: iso-8859-15" port)
+                 (write-line test-string port)
+                 (close-port port)
+                 (let* ((in-port (open-input-file filename
+                                                  #:guess-encoding #t))
+                        (line1 (read-line in-port))
+                        (line2 (read-line in-port)))
+                   (close-port in-port)
+                   (delete-file filename)
+                   (string=? line2 test-string)))))
+
+(with-test-prefix "keyword arguments for file openers"
+  (with-fluids ((%default-port-encoding "UTF-8"))
+    (let ((filename (test-file)))
+
+      (with-test-prefix "write #:encoding"
+
+        (pass-if-equal "open-file"
+            #vu8(116 0 101 0 115 0 116 0)
+            (let ((port (open-file filename "w"
+                                   #:encoding "UTF-16LE")))
+              (display "test" port)
+              (close-port port))
+            (let* ((port (open-file filename "rb"))
+                   (bv (get-bytevector-all port)))
+              (close-port port)
+              bv))
+
+        (pass-if-equal "open-output-file"
+            #vu8(116 0 101 0 115 0 116 0)
+            (let ((port (open-output-file filename
+                                          #:encoding "UTF-16LE")))
+              (display "test" port)
+              (close-port port))
+            (let* ((port (open-file filename "rb"))
+                   (bv (get-bytevector-all port)))
+              (close-port port)
+              bv))
+
+        (pass-if-equal "call-with-output-file"
+            #vu8(116 0 101 0 115 0 116 0)
+            (call-with-output-file filename
+              (lambda (port)
+                (display "test" port))
+              #:encoding "UTF-16LE")
+            (let* ((port (open-file filename "rb"))
+                   (bv (get-bytevector-all port)))
+              (close-port port)
+              bv))
+
+        (pass-if-equal "with-output-to-file"
+            #vu8(116 0 101 0 115 0 116 0)
+            (with-output-to-file filename
+              (lambda ()
+                (display "test"))
+              #:encoding "UTF-16LE")
+            (let* ((port (open-file filename "rb"))
+                   (bv (get-bytevector-all port)))
+              (close-port port)
+              bv))
+
+        (pass-if-equal "with-error-to-file"
+            #vu8(116 0 101 0 115 0 116 0)
+            (with-error-to-file
+             filename
+             (lambda ()
+               (display "test" (current-error-port)))
+             #:encoding "UTF-16LE")
+            (let* ((port (open-file filename "rb"))
+                   (bv (get-bytevector-all port)))
+              (close-port port)
+              bv)))
+
+      (with-test-prefix "write #:binary"
+
+        (pass-if-equal "open-output-file"
+            "ISO-8859-1"
+          (let* ((port (open-output-file filename #:binary #t))
+                 (enc (port-encoding port)))
+            (close-port port)
+            enc))
+
+        (pass-if-equal "call-with-output-file"
+            "ISO-8859-1"
+          (call-with-output-file filename port-encoding #:binary #t))
+
+        (pass-if-equal "with-output-to-file"
+            "ISO-8859-1"
+          (with-output-to-file filename
+            (lambda () (port-encoding (current-output-port)))
+            #:binary #t))
+
+        (pass-if-equal "with-error-to-file"
+            "ISO-8859-1"
+          (with-error-to-file
+           filename
+           (lambda () (port-encoding (current-error-port)))
+           #:binary #t)))
+
+      (with-test-prefix "read #:encoding"
+
+        (pass-if-equal "open-file read #:encoding"
+            "test"
+          (call-with-output-file filename
+            (lambda (port)
+              (put-bytevector port #vu8(116 0 101 0 115 0 116 0))))
+          (let* ((port (open-file filename "r" #:encoding "UTF-16LE"))
+                 (str  (read-string port)))
+            (close-port port)
+            str))
+
+        (pass-if-equal "open-input-file #:encoding"
+            "test"
+          (call-with-output-file filename
+            (lambda (port)
+              (put-bytevector port #vu8(116 0 101 0 115 0 116 0))))
+          (let* ((port (open-input-file filename #:encoding "UTF-16LE"))
+                 (str  (read-string port)))
+            (close-port port)
+            str))
+
+        (pass-if-equal "call-with-input-file #:encoding"
+            "test"
+          (call-with-output-file filename
+            (lambda (port)
+              (put-bytevector port #vu8(116 0 101 0 115 0 116 0))))
+          (call-with-input-file filename
+            read-string
+            #:encoding "UTF-16LE"))
+
+        (pass-if-equal "with-input-from-file #:encoding"
+            "test"
+          (call-with-output-file filename
+            (lambda (port)
+              (put-bytevector port #vu8(116 0 101 0 115 0 116 0))))
+          (with-input-from-file filename
+            read-string
+            #:encoding "UTF-16LE")))
+
+      (with-test-prefix "read #:binary"
+
+        (pass-if-equal "open-input-file"
+            "ISO-8859-1"
+          (let* ((port (open-input-file filename #:binary #t))
+                 (enc (port-encoding port)))
+            (close-port port)
+            enc))
+
+        (pass-if-equal "call-with-input-file"
+            "ISO-8859-1"
+          (call-with-input-file filename port-encoding #:binary #t))
+
+        (pass-if-equal "with-input-from-file"
+            "ISO-8859-1"
+          (with-input-from-file filename
+            (lambda () (port-encoding (current-input-port)))
+            #:binary #t)))
+
+      (with-test-prefix "#:guess-encoding with coding declaration"
+
+        (pass-if-equal "open-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda ()
+              (write-line "test")
+              (write-line "; coding: ISO-8859-15")
+              (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (let* ((port (open-file filename "r"
+                                  #:guess-encoding #t
+                                  #:encoding "UTF-16LE"))
+                 (str (begin (read-line port)
+                             (read-line port)
+                             (read-line port))))
+            (close-port port)
+            str))
+
+        (pass-if-equal "open-input-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda ()
+              (write-line "test")
+              (write-line "; coding: ISO-8859-15")
+              (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (let* ((port (open-input-file filename
+                                        #:guess-encoding #t
+                                        #:encoding "UTF-16LE"))
+                 (str (begin (read-line port)
+                             (read-line port)
+                             (read-line port))))
+            (close-port port)
+            str))
+
+        (pass-if-equal "call-with-input-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda ()
+              (write-line "test")
+              (write-line "; coding: ISO-8859-15")
+              (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (call-with-input-file filename
+            (lambda (port)
+              (read-line port)
+              (read-line port)
+              (read-line port))
+            #:guess-encoding #t
+            #:encoding "UTF-16LE"))
+
+        (pass-if-equal "with-input-from-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda ()
+              (write-line "test")
+              (write-line "; coding: ISO-8859-15")
+              (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (with-input-from-file filename
+            (lambda ()
+              (read-line)
+              (read-line)
+              (read-line))
+            #:guess-encoding #t
+            #:encoding "UTF-16LE")))
+
+      (with-test-prefix "#:guess-encoding without coding declaration"
+
+        (pass-if-equal "open-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda () (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (let* ((port (open-file filename "r"
+                                  #:guess-encoding #t
+                                  #:encoding "ISO-8859-15"))
+                 (str (read-line port)))
+            (close-port port)
+            str))
+
+        (pass-if-equal "open-input-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda () (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (let* ((port (open-input-file filename
+                                        #:guess-encoding #t
+                                        #:encoding "ISO-8859-15"))
+                 (str (read-line port)))
+            (close-port port)
+            str))
+
+        (pass-if-equal "call-with-input-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda () (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (call-with-input-file filename
+            read-line
+            #:guess-encoding #t
+            #:encoding "ISO-8859-15"))
+
+        (pass-if-equal "with-input-from-file"
+            "€100"
+          (with-output-to-file filename
+            (lambda () (write-line "€100"))
+            #:encoding "ISO-8859-15")
+          (with-input-from-file filename
+            read-line
+            #:guess-encoding #t
+            #:encoding "ISO-8859-15")))
+
+      (delete-file filename))))
+
 ;;; ungetting characters and strings.
 (with-input-from-string "walk on the moon\nmoon"
                        (lambda ()
     (pass-if "output check"
             (string=? text result)))
 
-  (pass-if "encoding failure leads to exception"
-    ;; Prior to 2.0.6, this would trigger a deadlock in `scm_mkstrport'.
-    ;; See the discussion at <http://bugs.gnu.org/11197>, for details.
-    (catch 'encoding-error
-      (lambda ()
-        (with-fluids ((%default-port-encoding "ISO-8859-1"))
-          (let ((p (open-input-string "λ")))      ; raise an exception
-            #f)))
-      (lambda (key . rest)
-        #t)
-      (lambda (key . rest)
-        ;; At this point, the port-table mutex used to be still held,
-        ;; hence the deadlock.  This situation would occur when trying
-        ;; to print a backtrace, for instance.
-        (input-port? (open-input-string "foo")))))
-
-  (pass-if "%default-port-encoding is honored"
-    (let ((encodings '("UTF-8" "UTF-16" "ISO-8859-1" "ISO-8859-3")))
-      (equal? (map (lambda (e)
-                     (with-fluids ((%default-port-encoding e))
-                       (call-with-output-string
-                         (lambda (p)
-                           (and (string=? e (port-encoding p))
-                                (display (port-encoding p) p))))))
-                   encodings)
-              encodings)))
+  (pass-if "%default-port-encoding is ignored"
+    (let ((str "ĉu bone?"))
+      ;; Latin-1 cannot represent ‘ĉ’.
+      (with-fluids ((%default-port-encoding "ISO-8859-1"))
+        (string=? (call-with-output-string
+                   (lambda (p)
+                     (set-port-conversion-strategy! p 'substitute)
+                     (display str p)))
+                  "ĉu bone?"))))
 
   (pass-if "%default-port-conversion-strategy is honored"
     (let ((strategies '(error substitute escape)))
               (map symbol->string strategies))))
 
   (pass-if "suitable encoding [latin-1]"
-    (let ((str "hello, world"))
-      (with-fluids ((%default-port-encoding "ISO-8859-1"))
-        (equal? str
-                (with-output-to-string
-                  (lambda ()
-                    (display str)))))))
+    (let ((str "hello, world")
+          (encoding "ISO-8859-1"))
+      (equal? str
+              (call-with-output-string
+               (lambda (p)
+                 (set-port-encoding! p encoding)
+                 (display str p))))))
 
   (pass-if "suitable encoding [latin-3]"
-    (let ((str "ĉu bone?"))
-      (with-fluids ((%default-port-encoding "ISO-8859-3"))
-        (equal? str
-                (with-output-to-string
-                  (lambda ()
-                    (display str)))))))
+    (let ((str "ĉu bone?")
+          (encoding "ISO-8859-3"))
+      (equal? str
+              (call-with-output-string
+               (lambda (p)
+                 (set-port-encoding! p encoding)
+                 (display str p))))))
 
   (pass-if "wrong encoding, error"
     (let ((str "ĉu bone?"))
       (catch 'encoding-error
         (lambda ()
-          ;; Latin-1 cannot represent ‘ĉ’.
-          (with-fluids ((%default-port-encoding "ISO-8859-1")
-                        (%default-port-conversion-strategy 'error))
-            (with-output-to-string
-              (lambda ()
-                (display str))))
-          #f)                            ; so the test really fails here
+          (with-fluids ((%default-port-conversion-strategy 'error))
+            (call-with-output-string
+             (lambda (p)
+               ;; Latin-1 cannot represent ‘ĉ’.
+               (set-port-encoding! p "ISO-8859-1")
+               (display str p))))
+          #f)                           ; so the test really fails here
         (lambda (key subr message errno port chr)
           (and (eqv? chr #\ĉ)
                (string? (strerror errno)))))))
 
   (pass-if "wrong encoding, substitute"
     (let ((str "ĉu bone?"))
-      (with-fluids ((%default-port-encoding "ISO-8859-1"))
-        (string=? (with-output-to-string
-                    (lambda ()
-                      (set-port-conversion-strategy! (current-output-port)
-                                                     'substitute)
-                      (display str)))
-                  "?u bone?"))))
+      (string=? (call-with-output-string
+                 (lambda (p)
+                   (set-port-encoding! p "ISO-8859-1")
+                   (set-port-conversion-strategy! p 'substitute)
+                   (display str p)))
+                "?u bone?")))
 
   (pass-if "wrong encoding, escape"
     (let ((str "ĉu bone?"))
-      (with-fluids ((%default-port-encoding "ISO-8859-1"))
-        (string=? (with-output-to-string
-                    (lambda ()
-                      (set-port-conversion-strategy! (current-output-port)
-                                                     'escape)
-                      (display str)))
-                  "\\u0109u bone?"))))
-
-  (pass-if "peek-char [latin-1]"
-    (let ((p (with-fluids ((%default-port-encoding #f))
-               (open-input-string "hello, world"))))
-      (and (char=? (peek-char p) #\h)
-           (char=? (peek-char p) #\h)
-           (char=? (peek-char p) #\h)
-           (= (port-line p) 0)
-           (= (port-column p) 0))))
-
-  (pass-if "peek-char [utf-8]"
-    (let ((p (with-fluids ((%default-port-encoding "UTF-8"))
-               (open-input-string "안녕하세요"))))
-      (and (char=? (peek-char p) #\안)
-           (char=? (peek-char p) #\안)
-           (char=? (peek-char p) #\안)
-           (= (port-line p) 0)
-           (= (port-column p) 0))))
-
-  (pass-if "peek-char [utf-16]"
-    (let ((p (with-fluids ((%default-port-encoding "UTF-16BE"))
-               (open-input-string "안녕하세요"))))
+      (string=? (call-with-output-string
+                 (lambda (p)
+                   (set-port-encoding! p "ISO-8859-1")
+                   (set-port-conversion-strategy! p 'escape)
+                   (display str p)))
+                "\\u0109u bone?")))
+
+  (pass-if "peek-char"
+    (let ((p (open-input-string "안녕하세요")))
       (and (char=? (peek-char p) #\안)
            (char=? (peek-char p) #\안)
            (char=? (peek-char p) #\안)
       (set-port-encoding! p "does-not-exist")
       (read p)))
 
-  (pass-if-exception "%default-port-encoding, wrong encoding"
-    exception:miscellaneous-error
-    (read (with-fluids ((%default-port-encoding "does-not-exist"))
-            (open-input-string "")))))
+  (let ((filename (test-file)))
+    (with-output-to-file filename (lambda () (write 'test)))
+
+    (pass-if-exception "%default-port-encoding, wrong encoding"
+        exception:miscellaneous-error
+      (read (with-fluids ((%default-port-encoding "does-not-exist"))
+              (open-input-file filename))))
+
+    (delete-file filename)))
 
 ;;;
 ;;; port-for-each
 
 (with-test-prefix "setvbuf"
 
+  (pass-if-exception "closed port"
+      exception:wrong-type-arg
+    (let ((port (open-input-file "/dev/null")))
+      (close-port port)
+      (setvbuf port _IOFBF)))
+
+  (pass-if-exception "string port"
+      exception:wrong-type-arg
+    (let ((port (open-input-string "Hey!")))
+      (close-port port)
+      (setvbuf port _IOFBF)))
+
   (pass-if "line/column number preserved"
     ;; In Guile 2.0.5, `setvbuf' would erroneously decrease the port's
     ;; line and/or column number.
 
 \f
 
+(pass-if-equal "unget-bytevector"
+    #vu8(10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 200 201 202 203
+            1 2 3 4 251 253 254 255)
+  (let ((port (open-bytevector-input-port #vu8(1 2 3 4 251 253 254 255))))
+    (unget-bytevector port #vu8(200 201 202 203))
+    (unget-bytevector port #vu8(20 21 22 23 24))
+    (unget-bytevector port #vu8(10 11 12 13 14 15 16 17 18 19) 4)
+    (unget-bytevector port #vu8(10 11 12 13 14 15 16 17 18 19) 2 2)
+    (unget-bytevector port #vu8(10 11))
+    (get-bytevector-all port)))
+
+\f
+
 (with-test-prefix "unicode byte-order marks (BOMs)"
 
   (define (bv-read-test* encoding bv proc)
 
   (pass-if-equal "BOM not discarded unless at start of UTF-16 stream"
       "a\uFEFFb"
-    (let ((be (bv-read-test "utf-16" #vu8(#x00 #x61 #xFE #xFF #x00 #x62)))
-          (le (bv-read-test "utf-16" #vu8(#x61 #x00 #xFF #xFE #x62 #x00))))
-      (if (char=? #\a (string-ref be 0))
-          be
-          le)))
+    (bv-read-test "utf-16" #vu8(#x00 #x61 #xFE #xFF #x00 #x62)))
 
   (pass-if-equal "BOM discarded from start of UTF-16 stream (LE)"
       "a"
 
   (pass-if-equal "BOM not discarded unless at start of UTF-32 stream"
       "a\uFEFFb"
-    (let ((be (bv-read-test "UTF-32" #vu8(#x00 #x00 #x00 #x61
-                                               #x00 #x00 #xFE #xFF
-                                               #x00 #x00 #x00 #x62)))
-          (le (bv-read-test "UTF-32" #vu8(#x61 #x00 #x00 #x00
-                                               #xFF #xFE #x00 #x00
-                                               #x62 #x00 #x00 #x00))))
-      (if (char=? #\a (string-ref be 0))
-          be
-          le)))
+    (bv-read-test "UTF-32" #vu8(#x00 #x00 #x00 #x61
+                                     #x00 #x00 #xFE #xFF
+                                     #x00 #x00 #x00 #x62)))
 
   (pass-if-equal "BOM discarded from start of UTF-32 stream (LE)"
       "a"