;; Phase 14 — String ports + eof-object (deftest "eof-object" (deftest "eof-object is eof" (assert= true (eof-object? (eof-object)) "eof-object? returns true for eof-object")) (deftest "non-eof values are not eof" (assert= false (eof-object? nil) "nil is not eof") (assert= false (eof-object? "") "string is not eof") (assert= false (eof-object? 0) "zero is not eof") (assert= false (eof-object? false) "false is not eof")) (deftest "type-of eof-object" (assert= "eof-object" (type-of (eof-object)) "type-of eof-object is eof-object"))) (deftest "open-input-string" (deftest "creates input port" (let (p (open-input-string "hello")) (assert= true (port? p) "is a port") (assert= true (input-port? p) "is an input port") (assert= false (output-port? p) "is not an output port"))) (deftest "type-of input port" (let (p (open-input-string "x")) (assert= "input-port" (type-of p) "type-of is input-port")))) (deftest "open-output-string" (deftest "creates output port" (let (p (open-output-string)) (assert= true (port? p) "is a port") (assert= true (output-port? p) "is an output port") (assert= false (input-port? p) "is not an input port"))) (deftest "type-of output port" (let (p (open-output-string)) (assert= "output-port" (type-of p) "type-of is output-port")))) (deftest "read-char" (deftest "reads chars sequentially" (let (p (open-input-string "ab")) (let (c1 (read-char p)) (assert= true (char? c1) "first result is char") (assert= 97 (char->integer c1) "first char is a")))) (deftest "reads second char" (let (p (open-input-string "ab")) (read-char p) (let (c2 (read-char p)) (assert= true (char? c2) "second result is char") (assert= 98 (char->integer c2) "second char is b")))) (deftest "returns eof at end" (let (p (open-input-string "x")) (read-char p) (assert= true (eof-object? (read-char p)) "eof after last char"))) (deftest "empty string yields eof immediately" (let (p (open-input-string "")) (assert= true (eof-object? (read-char p)) "eof from empty string")))) (deftest "peek-char" (deftest "peeks without consuming" (let (p (open-input-string "x")) (let (c1 (peek-char p)) (let (c2 (peek-char p)) (assert= (char->integer c1) (char->integer c2) "peek twice gives same char"))))) (deftest "peek then read" (let (p (open-input-string "z")) (let (peeked (peek-char p)) (let (read (read-char p)) (assert= (char->integer peeked) (char->integer read) "peek and read agree"))))) (deftest "peek at end returns eof" (let (p (open-input-string "")) (assert= true (eof-object? (peek-char p)) "eof on empty peek")))) (deftest "read-line" (deftest "reads a single line" (let (p (open-input-string "hello")) (assert= "hello" (read-line p) "reads whole string as line"))) (deftest "reads line up to newline" (let (p (open-input-string "foo\nbar")) (assert= "foo" (read-line p) "first line is foo"))) (deftest "reads second line" (let (p (open-input-string "foo\nbar")) (read-line p) (assert= "bar" (read-line p) "second line is bar"))) (deftest "returns eof on empty port" (let (p (open-input-string "")) (assert= true (eof-object? (read-line p)) "eof on empty"))) (deftest "returns eof after last line" (let (p (open-input-string "hi")) (read-line p) (assert= true (eof-object? (read-line p)) "eof after reading")))) (deftest "write-char and get-output-string" (deftest "write single char" (let (p (open-output-string)) (write-char (make-char 65) p) (assert= "A" (get-output-string p) "write char A"))) (deftest "write multiple chars" (let (p (open-output-string)) (write-char (make-char 72) p) (write-char (make-char 105) p) (assert= "Hi" (get-output-string p) "write Hi")))) (deftest "write-string" (deftest "write a string to port" (let (p (open-output-string)) (write-string "hello" p) (assert= "hello" (get-output-string p) "write-string result"))) (deftest "multiple writes concatenate" (let (p (open-output-string)) (write-string "foo" p) (write-string "bar" p) (assert= "foobar" (get-output-string p) "concatenated writes")))) (deftest "get-output-string idempotent" (let (p (open-output-string)) (write-string "test" p) (assert= "test" (get-output-string p) "first call") (assert= "test" (get-output-string p) "second call same result"))) (deftest "char-ready?" (deftest "ready when chars available" (let (p (open-input-string "x")) (assert= true (char-ready? p) "ready with content"))) (deftest "not ready when empty" (let (p (open-input-string "")) (assert= false (char-ready? p) "not ready when empty")))) (deftest "close-port" (deftest "close input port" (let (p (open-input-string "hello")) (close-port p) (assert= true (eof-object? (read-char p)) "read after close gives eof"))) (deftest "close output port" (let (p (open-output-string)) (write-string "ok" p) (close-port p) (assert= "ok" (get-output-string p) "output preserved after close")))) (deftest "roundtrip string via ports" (let (in (open-input-string "abc")) (let (out (open-output-string)) (do (let (c1 (read-char in)) (when (not (eof-object? c1)) (write-char c1 out))) (let (c2 (read-char in)) (when (not (eof-object? c2)) (write-char c2 out))) (let (c3 (read-char in)) (when (not (eof-object? c3)) (write-char c3 out))) (assert= "abc" (get-output-string out) "roundtrip via ports")))))