(module io mzscheme
(require (planet "test.ss" ("schematics" "schemeunit.plt" 1)))
(require (planet "util.ss" ("schematics" "schemeunit.plt" 1)))
(require (planet "test.ss" ("dherman" "test.plt" 1)))
(require (lib "async-channel.ss"))
(require (lib "etc.ss"))
(require (lib "port.ss"))
(require (lib "inflate.ss"))
(require (lib "match.ss"))
(require/expose "../../io.ss" (ones-mask make-filter-input-port/debug))
(define test:with-output-to-string
(make-test-suite
"with-output-to-string"
(make-test-case "empty string"
(assert string=? (with-output-to-string (void)) ""))
(make-test-case "a few display operations"
(assert string=? (with-output-to-string
(display "hello")
(display ", ")
(display "world")
(display "!")
(newline))
"hello, world!\n"))))
(define test:ones-mask
(make-test-suite
"ones-mask"
(make-test-case "zero"
(assert = (ones-mask 0) 0))
(make-test-case "one through ten"
(let loop ([i 1])
(unless (>= i 10)
(let ([ans (ones-mask i)]
[expected (build-string (* 8 i) (lambda (x) #\1))])
(assert string=? (format "~b" ans) expected)))))))
(define test:bit-set?
(make-test-suite
"bit-set?"
(make-test-case ""
(assert-true (bit-set? 0 #b1)))
(make-test-case ""
(assert-true (bit-set? 1 #b10)))
(make-test-case ""
(assert-true (bit-set? 2 #b100)))
(make-test-case ""
(assert-true (bit-set? 9 #b1000000000)))
(make-test-case ""
(assert-false (bit-set? 0 #b11111111110)))
(make-test-case ""
(assert-false (bit-set? 1 #b11111111101)))
(make-test-case ""
(assert-false (bit-set? 2 #b11111111011)))
(make-test-case ""
(assert-false (bit-set? 9 #b10111111111)))
))
(define test:stretch-bytes
(make-test-suite
"stretch-bytes"
(make-test-case "stretch big-endian"
(assert bytes=? (stretch-bytes #"dave" 8 #t (char->integer #\X))
#"XXXXdave"))
(make-test-case "stretch small-endian"
(assert bytes=? (stretch-bytes #"dave" 8 #f (char->integer #\X))
#"daveXXXX"))
(make-test-case "stretch big-endian with default fill"
(assert bytes=? (stretch-bytes #"dave" 8 #t)
#"\0\0\0\0dave"))
(make-test-case "stretch small-endian with default fill"
(assert bytes=? (stretch-bytes #"dave" 8 #f)
#"dave\0\0\0\0"))
(make-test-case "stretch none big-endian"
(assert bytes=? (stretch-bytes #"dave" 4 #t)
#"dave"))
(make-test-case "stretch none small-endian"
(assert bytes=? (stretch-bytes #"dave" 4 #f)
#"dave"))
(make-test-case "stretch not enough big-endian"
(assert-exn exn:fail?
(lambda ()
(stretch-bytes #"dave" 2 #t))))
(make-test-case "stretch not enough small-endian"
(assert-exn exn:fail?
(lambda ()
(stretch-bytes #"dave" 3 #f))))
))
(define (integer->bytes->integer n big-endian?)
(bytes->integer (integer->integer-bytes n 4 #f big-endian?)
#t
big-endian?))
(define test:bytes->integer/unsigned
(make-test-suite
"bytes->integer (unsigned)"
(make-test-case "No bytes - big-endian"
(assert = (bytes->integer (bytes) #f #t) 0))
(make-test-case "No bytes - small-endian"
(assert = (bytes->integer (bytes) #f #f) 0))
(make-test-case "Simple test 1 - big-endian"
(assert = (bytes->integer (bytes 2 1) #f #t) 513))
(make-test-case "Simple test 1 - small-endian"
(assert = (bytes->integer (bytes 2 1) #f #f) 258))
(make-test-case "Reverse endianness"
(assert = (bytes->integer (bytes 24 28 200 12) #f #t)
(bytes->integer (bytes 12 200 28 24) #f #f)))
(make-test-case "compatible with integer->integer-bytes"
(assert = (integer->bytes->integer 2461357 #t) 2461357))
))
(define test:bytes->integer/signed
(make-test-suite
"bytes->integer (signed)"
(make-test-case "-1 in one byte"
(assert = (bytes->integer #"\377" #t #t) -1))
(make-test-case "-1 in two bytes"
(assert = (bytes->integer #"\377\377" #t #t) -1))
(make-test-case "-1 in four bytes"
(assert = (bytes->integer #"\377\377\377\377" #t #t) -1))
(make-test-case "-20"
(assert = (bytes->integer #"\377\354" #t #t) -20))
(make-test-case "-38274773"
(assert = (bytes->integer #"\375\267\371+" #t #t) -38274773))
(make-test-case "-3333333333333333333"
(assert =
(bytes->integer #"\321\275\236\376|\262\252\253" #t #t)
-3333333333333333333))
))
(define test:integer->bytes/unsigned
(make-test-suite
"integer->bytes (unsigned)"
(make-test-case "1"
(assert bytes=? (integer->bytes 1 #f #t 1) #"\001"))
(make-test-case "255"
(assert bytes=? (integer->bytes 255 #f #t 2) #"\000\377"))
(make-test-case "3527688"
(assert bytes=? (integer->bytes 3527688 #f #t 4) #"\0005\324\b"))
))
(define test:integer->bytes/signed
(make-test-suite
"integer->bytes (signed)"
(make-test-case "-1"
(assert bytes=? (integer->bytes -1 #t #t 1) #"\377"))
(make-test-case "-255"
(assert bytes=? (integer->bytes -255 #t #t 2) #"\377\1"))
(make-test-case "-3527688"
(assert bytes=? (integer->bytes -3527688 #t #t 4) #"\377\312+\370"))
))
(define-struct running-script (thread channel port file-port))
(define (interrupt-script running)
(close-input-port (running-script-file-port running)))
(define (start-script path)
(let* ([history (make-async-channel 100)]
[file-in (open-input-file path)]
[filter-in (make-filter-input-port/debug inflate file-in #f history)])
(file-position file-in #x26)
(make-running-script
(thread
(lambda ()
(let ([handler (lambda (e)
(sleep 1)
(async-channel-put history `(exn main-thread ,e)))])
(with-handlers ([exn? handler])
(async-channel-put history 'trying-to-read)
(let loop ([i 0])
(when (> i 4440)
(async-channel-put history `(reading-line ,i)))
(cond
[(read-line filter-in 'any)
=> (lambda (line)
(unless (eof-object? line)
(loop (add1 i))))]))
(async-channel-put history 'done-reading)))))
history
filter-in
file-in)))
(define (script-wait running)
(thread-wait (running-script-thread running))
(close-input-port (running-script-port running))
(close-input-port (running-script-file-port running))
(let loop ([result '()])
(let ([event (async-channel-try-get (running-script-channel running))])
(if (not event)
(reverse result)
(loop (cons event result))))))
(define (transcript-element=? act exp)
(match exp
[('reading-line i)
(match act
[('reading-line j) (= i j)]
[_ #f])]
[('exn context correct-type?)
(match act
[('exn context* val)
(and (eq? context context*)
(correct-type? val))]
[_ #f])]
[_ (eq? exp act)]))
(define (transcript=? act exp)
(and (= (length act) (length exp))
(andmap transcript-element=? act exp)))
(define (assert-script running expected)
(let ([transcript (script-wait running)])
(assert transcript=? transcript expected
(format "expected: ~v, actual: ~v" expected transcript))))
(define (make-broken-copy from to k)
(with-input-from-file from
(lambda ()
(let ([in (make-limited-input-port (current-input-port) k)])
(with-output-to-file to
(lambda ()
(copy-port in (current-output-port))))))))
(define test:make-filter-input-port
(make-test-suite
"make-filter-input-port tests (ooooh.. concurrency..)"
(make-test-case "relatively big file"
(in-this-directory
(assert-script
(start-script (build-path "examples" "big.zip"))
'(trying-to-read
done-transform
(reading-line 4441)
(reading-line 4442)
(reading-line 4443)
(reading-line 4444)
done-reading))))
(make-test-case "interrupting mid-transform"
(in-this-directory
(let ([running (start-script (build-path "examples" "big.zip"))])
(interrupt-script running)
(assert-script running
`(trying-to-read
(exn transformer ,exn:fail?)
(exn main-thread ,exn:fail?))))))
(make-test-case "exception mid-transform"
(in-this-directory
(in-new-directory "sandbox"
(make-broken-copy (build-path 'up "examples" "big.zip")
"broken.zip"
42440)
(assert-script
(start-script "broken.zip")
`(trying-to-read
(exn transformer ,exn:fail:contract?)
(exn main-thread ,exn:fail:contract?))))))
))
(define io-tests
(make-test-suite
"All io.ss tests"
test:ones-mask
test:with-output-to-string
test:bit-set?
test:stretch-bytes
test:bytes->integer/unsigned
test:bytes->integer/signed
test:integer->bytes/unsigned
test:integer->bytes/signed
test:make-filter-input-port
))
(provide io-tests))