;;;; rdelim.test --- Delimited I/O.      -*- mode: scheme; coding: utf-8; -*-
;;;; Ludovic Courtès <ludo@gnu.org>
;;;;
;;;; 	Copyright (C) 2011, 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
;;;; 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-suite test-rdelim)
  #:use-module (ice-9 rdelim)
  #:use-module ((rnrs io ports) #:select (open-bytevector-input-port get-u8))
  #:use-module (test-suite lib)
  #:use-module (test-suite guile-test))

(with-test-prefix "read-line"

  (pass-if "one line"
    (let* ((s "hello, world")
           (p (open-input-string s)))
      (and (string=? s (read-line p))
           (eof-object? (read-line p)))))

  (pass-if "two lines, trim"
    (let* ((s "foo\nbar\n")
           (p (open-input-string s)))
      (and (equal? (string-tokenize s)
                   (list (read-line p) (read-line p)))
           (eof-object? (read-line p)))))

  (pass-if "two lines, concat"
    (let* ((s "foo\nbar\n")
           (p (open-input-string s)))
      (and (equal? '("foo\n" "bar\n")
                   (list (read-line p 'concat)
                         (read-line p 'concat)))
           (eof-object? (read-line p)))))

  (pass-if "two lines, peek"
    (let* ((s "foo\nbar\n")
           (p (open-input-string s)))
      (and (equal? '("foo" #\newline "bar" #\newline)
                   (list (read-line p 'peek) (read-char p)
                         (read-line p 'peek) (read-char p)))
           (eof-object? (read-line p)))))

  (pass-if "two lines, split"
    (let* ((s "foo\nbar\n")
           (p (open-input-string s)))
      (and (equal? '(("foo" . #\newline)
                     ("bar" . #\newline))
                   (list (read-line p 'split)
                         (read-line p 'split)))
           (eof-object? (read-line p)))))

  (pass-if "two Greek lines, trim"
    (let* ((s "λαμβδα\nμυ\n")
           (p (open-input-string s)))
      (and (equal? (string-tokenize s)
                   (list (read-line p) (read-line p)))
           (eof-object? (read-line p)))))

  (pass-if "decoding error"
    (let ((p (open-bytevector-input-port #vu8(65 255 66 67 68))))
      (set-port-encoding! p "UTF-8")
      (set-port-conversion-strategy! p 'error)
      (catch 'decoding-error
        (lambda ()
          (read-line p)
          #f)
        (lambda (key subr message err port)
          (and (eq? port p)
               (eqv? (get-u8 p) 255)
               (string=? (read-line p) "BCD")
               (eof-object? (read-line p)))))))

  (pass-if "decoding error, substitute"
    (let ((p (open-bytevector-input-port #vu8(65 255 66 67 68))))
      (set-port-encoding! p "UTF-8")
      (set-port-conversion-strategy! p 'substitute)
      (and (string=? (read-line p) "A\uFFFDBCD")
           (eof-object? (read-line p))))))


(with-test-prefix "read-delimited"

  (pass-if "delimiter hit"
    (let ((p (open-input-string "hello, world!")))
      (and (string=? "hello" (read-delimited ",.;" p))
           (string=? " world!" (read-delimited ",.;" p))
           (eof-object? (read-delimited ",.;" p)))))

  (pass-if "delimiter hit, split"
    (equal? '("hello" . #\,)
            (read-delimited ",.;"
                            (open-input-string "hello, world!")
                            'split)))

  (pass-if "delimiter hit, concat"
    (equal? '"hello,"
            (read-delimited ",.;" (open-input-string "hello, world!")
                            'concat)))

  (pass-if "delimiter hit, peek"
    (let ((p (open-input-string "hello, world!")))
      (and (string=? "hello" (read-delimited ",.;" p 'peek))
           (char=? #\, (peek-char p)))))

  (pass-if "eof"
    (eof-object? (read-delimited "}{" (open-input-string "")))))


(with-test-prefix "read-delimited!"

  (pass-if "delimiter hit"
    (let ((s (make-string 123))
          (p (open-input-string "hello, world!")))
      (and (= 5 (read-delimited! ",.;" s p))
           (string=? (substring s 0 5) "hello")
           (= 7 (read-delimited! ",.;" s p))
           (string=? (substring s 0 7) " world!")
           (eof-object? (read-delimited! ",.;" s p)))))

  (pass-if "delimiter hit, start+end"
    (let ((s (make-string 123))
          (p (open-input-string "hello, world!")))
      (and (= 5 (read-delimited! ",.;" s p 'trim 10 30))
           (string=? (substring s 10 15) "hello"))))

  (pass-if "delimiter hit, split"
    (let ((s (make-string 123)))
      (and (equal? '(5 . #\,)
                   (read-delimited! ",.;" s
                                    (open-input-string "hello, world!")
                                    'split))
           (string=? (substring s 0 5) "hello"))))

  (pass-if "delimiter hit, concat"
    (let ((s (make-string 123)))
      (and (= 6 (read-delimited! ",.;" s
                                 (open-input-string "hello, world!")
                                 'concat))
           (string=? (substring s 0 6) "hello,"))))

  (pass-if "delimiter hit, peek"
    (let ((s (make-string 123))
          (p (open-input-string "hello, world!")))
      (and (= 5 (read-delimited! ",.;" s p 'peek))
           (string=? (substring s 0 5) "hello")
           (char=? #\, (peek-char p)))))

  (pass-if "string too small"
    (let ((s (make-string 7)))
      (and (= 7 (read-delimited! "}{" s
                                 (open-input-string "hello, world!")))
           (string=? s "hello, "))))

  (pass-if "string too small, start+end"
    (let ((s (make-string 123)))
      (and (= 7 (read-delimited! "}{" s
                                 (open-input-string "hello, world!")
                                 'trim
                                 70 77))
           (string=? (substring s 70 77) "hello, "))))

  (pass-if "string too small, split"
    (let ((s (make-string 7)))
      (and (equal? '(7 . #f)
                   (read-delimited! "}{" s
                                    (open-input-string "hello, world!")
                                    'split))
           (string=? s "hello, "))))

  (pass-if "eof"
    (eof-object? (read-delimited! ":" (make-string 7)
                                  (open-input-string ""))))

  (pass-if "eof, split"
    (eof-object? (read-delimited! ":" (make-string 7)
                                  (open-input-string "")))))

(with-test-prefix "read-string"

  (pass-if "short string"
    (let* ((s "hello, world!")
           (p (open-input-string s)))
      (and (string=? (read-string p) s)
           (string=? (read-string p) ""))))

  (pass-if "100 chars"
    (let* ((s (make-string 100 #\space))
           (p (open-input-string s)))
      (and (string=? (read-string p) s)
           (string=? (read-string p) ""))))

  (pass-if "longer than 100 chars"
    (let* ((s (string-concatenate (make-list 20 "hello, world!")))
           (p (open-input-string s)))
      (and (string=? (read-string p) s)
           (string=? (read-string p) ""))))

  (pass-if-equal "longer than 100 chars, with limit"
      "hello, world!"
    (let* ((s (string-concatenate (make-list 20 "hello, world!")))
           (p (open-input-string s)))
      (read-string p 13))))

(with-test-prefix "read-string!"

  (pass-if "buf smaller"
    (let* ((s "hello, world!")
           (len (1- (string-length s)))
           (buf (make-string len #\.))
           (p (open-input-string s)))
      (and (= (read-string! buf p) len)
           (string=? buf (substring s 0 len))
           (= (read-string! buf p) 1)
           (string=? (substring buf 0 1) (substring s len)))))

  (pass-if "buf right size"
    (let* ((s "hello, world!")
           (len (string-length s))
           (buf (make-string len #\.))
           (p (open-input-string s)))
      (and (= (read-string! buf p) len)
           (string=? buf (substring s 0 len))
           (= (read-string! buf p) 0)
           (string=? buf (substring s 0 len)))))

  (pass-if "buf bigger"
    (let* ((s "hello, world!")
           (len (string-length s))
           (buf (make-string (1+ len) #\.))
           (p (open-input-string s)))
      (and (= (read-string! buf p) len)
           (string=? (substring buf 0 len) s)
           (= (read-string! buf p) 0)
           (string=? (substring buf 0 len) s)
           (string=? (substring buf len) ".")))))


(with-test-prefix "for-line-in-file"

  (define (test-file)
    (data-file-name "ports-test.tmp"))

  (let ((lst '())
        (lines '())
        (string "line1\nline2\nline3")
        (filename (test-file)))
    (call-with-input-string
        "A\0B\0C"
      (lambda (port)
        (pass-if "for-delimited-from-port parses stream correctly"
          (for-delimited-from-port port
                                   (lambda (entry)
                                     (set! lst (cons entry lst)))
                                   #:delims "\0")
          (equal? lst '("C" "B" "A")))))
    (let ((port (open-output-file filename)))
      (display string port)
      (close-port port))
    (pass-if "for-line-in-file parses file correctly"
      (for-line-in-file filename
                        (lambda (line)
                          (set! lines (cons line lines))))
      (equal? lines '("line3" "line2" "line1"))))

  (delete-file (test-file))
  )

;;; Local Variables:
;;; eval: (put 'with-test-prefix 'scheme-indent-function 1)
;;; eval: (put 'pass-if 'scheme-indent-function 1)
;;; End:
