test/test-file-utils.rkt
#lang racket
(provide file-utils-tests test-all)

(require rackunit
         rackunit/gui
         "../file-utils.rkt")

(define file-utils-tests
  (test-suite 
   "File-Utils Library"
   (let ((testfolder (build-path "testfiles"))
         (testfile (build-path "testfiles" "testfile.txt")))
     
     (test-case
      "get-file-or-directory-name of directory"
      (check-equal? "testfiles" (get-file-or-directory-name testfolder)))
     
     (test-case
      "get-file-or-directory-name of file"
      (check-equal? (get-file-or-directory-name testfile)
                    "testfile.txt" ))
     
     (test-case
      "get-file-or-directory-name taking string"
      (check-equal? (get-file-or-directory-name (path->string (build-path "testfiles")))
                    "testfiles" ))
     
     (test-case
      "name-as-string of directory"
      (check-equal? (name-as-string testfolder) "testfiles"))
     
     (test-case
      "name-as-string of file"
      (check-equal? (name-as-string testfile) "testfile.txt"))
     
     (test-case
      "name-as-string taking string"
      (check-equal? (name-as-string testfolder)
                    "testfiles" ))
     
     (test-case
      "filename-main"
      (check-equal? (filename-main testfile)
                    "testfile"))
     
     (test-case 
      "filename-main without suffix"
      (check-equal? (filename-main testfolder)
                    "testfiles" ))
     
     (test-case
      "filename-main empty name"
      (check-equal? (filename-main (build-path "testfiles" ".mp3"))
                    ""))
     
     (test-case
      "filename-main empty string"
      (check-equal? (filename-main "") "" ))
     
     (test-case
      "filename-suffix"
      (check-equal? (filename-suffix testfile) "txt"))
     
     (test-case
      "filename-suffix string"
      (check-equal? (filename-suffix "hello.mp3") "mp3" ))
     
     (test-case
      "filename-suffix empty suffix"
      (check-equal? "" (filename-suffix "hello")))
     
     (test-case
      "filename-suffix empty malformed suffix"
      (check-equal? (filename-suffix "hello.") ""))
     
     (test-case 
      "filename-suffix empty"
      (check-equal? (filename-suffix "")  ""))
     
     (test-case 
      "filename-suffix multiple suffixes"
      (check-equal? (filename-suffix (build-path "testfiles" "picture.tif.mp3"))
                    "mp3"))
     
     (test-case
      "compose-name"
      (check-equal? (compose-name "test" 23 "txt")
                    "test-23.txt" ))
     
     (test-case
      "compose-name no number"
      (check-equal? (compose-name "test" #f "txt")
                    "test.txt"))
     
     (test-case
      "compose-name empty suffix"
      (check-equal? (compose-name "test" 23 "")
                    "test-23"))
     
     (test-case
      "compose-name no number, empty suffix"
      (check-equal? (compose-name "test" #f "")
                    "test"))
     
     (test-case
      "compose-name empty name"
      (check-equal? (compose-name "" 23 "txt")
                    "-23.txt"))
     
     (test-case
      "compose-name everything empty"
      (check-equal? "" (compose-name "" #f "")))
     
     (test-case
      "make-unique-name"
      (check-false (file-exists? (make-unique-name "testfile.txt"
                                                   testfolder))))
     
     (test-case
      "make-unique-name nonexistent file"
      (check-equal? (make-unique-name (build-path "testfiles" "humpty.bla")
                                      (build-path "testfiles"))
                    (build-path "testfiles" "humpty.bla")))
     
     (test-case
      "parent-directory"
      (check-equal? (parent-directory testfile) (path->complete-path testfolder)))
     
     (test-case
      "make-unique-path"
      (check-false (file-exists? (make-unique-path testfile))))
     
     (test-case
      "make-unique-path returns path"
      (check-true (path? (make-unique-path testfile))))
     
     (test-case
      "path-equal?"
      (check-true (path-equal? testfile (path->complete-path testfile))))
     
     (test-case
      "path-equal? does not depend on file existence"
      (check-true (path-equal? (build-path "testfiles" "foobar.txt") (path->complete-path (build-path "testfiles" "foobar.txt")))))
     
     (test-case
      "file=?"
      (check-true (file=? testfile testfile)))
     
     (test-case
      "file=? requires existent file"
      (check-false (file=? (build-path "testfiles" "foobar.txt") (build-path "testfiles" "foobar.txt") )))
     
     (test-case
      "move-directory-to/renaming"
      (let ((from (build-path "test-received"))
            (to (build-path "testfiles"))
            (chk (build-path "testfiles" "test-received")))
        (move-directory-to/renaming from to)
        (check-true (directory-exists? chk))))
     
     (test-case
      "move-directory-to/renaming revert changes"
      (let ((from (build-path "testfiles" "test-received"))
            (to (current-directory))
            (chk (build-path "test-received")))
        (move-directory-to/renaming from to)
        (check-true (directory-exists? chk))))
     
     (test-case
      "move-directory-to/renaming returns path"
      (let ((from (build-path "test-received"))
            (to (build-path "testfiles"))
            (chk (build-path "testfiles" "test-received")))
        (let ((p (move-directory-to/renaming from to)))
          (check-true (path? p)))))
     
     (test-case
      "move-directory-to/renaming returns path revert changes"
      (let ((from (build-path "testfiles" "test-received"))
            (to (current-directory))
            (chk (build-path "test-received")))
        (move-directory-to/renaming from to)
        (check-true (directory-exists? chk))))
     
     (test-case
      "copy-directories/renaming"
      (let ((from (current-directory))
            (to (build-path "testfiles"))
            (chk (build-path "testfiles" "test-received")))
        (copy-directories/renaming from to (lambda (p) (equal? (filename-main p) "test-received")))
        (let ((result (directory-exists? chk)))
          (delete-directory chk)
          (check-true result))))
     
     (test-case 
      "is-visible? visible file"
      (check-true (file-is-visible? testfile)))
     
     (test-case
      "is-visible? invisible file"
      (check-false (file-is-visible? (build-path testfolder ".info"))))
     
     (test-case
      "count-lines mixed"
      (let ((in (open-input-string "\n\rhello\n\rworld\n")))
        (check-equal? (count-lines in) 3)))
     
     (test-case
      "count-lines empty"
      (let ((in (open-input-string "")))
        (check-equal? (count-lines in) 0)))
     
     (test-case
      "count-lines LF"
      (let ((in (open-input-string "\n\n\n\n")))
        (check-equal? (count-lines in) 4)))
     
     (test-case
      "count-lines one line"
      (let ((in (open-input-string "\n\rhello")))
        (check-equal? (count-lines in) 2)))
     
     (test-case
      "count-lines one line"
      (let ((in (open-input-string "\n\r\n\n\nhello")))
        (check-equal? (count-lines in) 5)))
     
     (test-case
      "rename-or-copy-file"
      (let ((source testfile)
            (target (build-path "test-received" "testfile.txt")))
        (rename-or-copy-file source target)
        (check-true (file-exists? target))))
     
     (test-case
      "rename-or-copy-file source moved"
      (check-false (file-exists? (build-path "testfiles" "testfile.txt"))))
     
     (test-case
      "rename-or-copy-file move back"
      (let ((source (build-path "test-received" "testfile.txt"))
            (target testfile))
        (rename-or-copy-file source target)
        (check-true (file-exists? target))))
     
     (test-case
      "rename-or-copy-file original moved (2)"
      (check-false (file-exists? (build-path "test-received" "testfile.txt"))))
     
     (test-case
      "rename-or-copy-directory"
      (let ((from (build-path "test-received"))
            (to (build-path "test-received2")))
        (rename-or-copy-directory from to)
        (check-true (directory-exists? to))))
     
     (test-case
      "rename-or-copy-directory revert changes"
      (let ((from (build-path "test-received2"))
            (to (build-path "test-received")))
        (rename-or-copy-directory from to)
        (check-true (directory-exists? to))))
     
     (test-case
      "file-equal? identical file"
      (check-true (file-equal? testfile testfile)))
     
     (test-case
      "file-equal? two copies"
      (let ((source testfile)
            (target (build-path "test-received" "testfile.txt")))
        (copy-file source target)
        (check-true (file-equal? source target))
        (delete-file target)))
     
     (test-case 
      "file-equal? files do not exist"
      (check-exn exn:fail? (lambda () (file-equal? (build-path "testfiles" "nonexistent")
                                                   (build-path "testfiles" "nonexistent")))))
     
     (test-case 
      "file-equal? different files, same size"
      (let ((source testfile)
            (target (build-path "test-received" "testfile.txt")))
        (copy-file source target)
        (let ((out (open-output-file target #:mode 'binary #:exists 'update)))
          (file-position out 10)
          (write-byte 72 out)
          (close-output-port out))
        (check-false (file-equal? source target))
        (delete-file target)))
     )))

(define (setup)
  (when (directory-exists? (build-path "test-received"))
    (delete-directory/files (build-path "test-received")))
  (make-directory (build-path "test-received")))

(define (test-all)
  (setup)
  (test/gui file-utils-tests))