;; ========================================================================== ;; test-text-layout.sx — Tests for Pretext (DOM-free text layout) ;; ========================================================================== ;; All tests use fixed widths — no IO, pure arithmetic verification. ;; -------------------------------------------------------------------------- ;; Line breaking ;; -------------------------------------------------------------------------- (defsuite "line-badness" (deftest "exact fit has zero badness" (assert-equal 0 (line-badness 100 100))) (deftest "underfull line has positive badness" (assert (> (line-badness 50 100) 0))) (deftest "overfull line has large badness" (assert (> (line-badness 110 100) 10000)))) (defsuite "compute-demerits" (deftest "zero badness and penalty gives 1" (assert-equal 1 (compute-demerits 0 0))) (deftest "badness contributes quadratically" (assert-equal 121 (compute-demerits 10 0))) (deftest "penalty adds independently" (assert-equal 26 (compute-demerits 0 5)))) (defsuite "sum-widths" (deftest "single word" (assert-equal 10 (sum-widths (list 10 20 30) 5 0 1))) (deftest "two words with space" (assert-equal 35 (sum-widths (list 10 20 30) 5 0 2))) (deftest "three words with spaces" (assert-equal 70 (sum-widths (list 10 20 30) 5 0 3))) (deftest "subset range" (assert-equal 55 (sum-widths (list 10 20 30) 5 1 3)))) (defsuite "break-lines" (deftest "empty input" (assert-equal (list) (break-lines (list) 5 100))) (deftest "all words fit on one line" (let ((result (break-lines (list 10 20 30) 5 100))) (assert-equal 1 (len result)) (assert-equal (list 0 3) (first result)))) (deftest "forced two-line break" (let ((result (break-lines (list 40 40) 5 50))) (assert-equal 2 (len result)) (assert-equal (list 0 1) (first result)) (assert-equal (list 1 2) (nth result 1)))) (deftest "three lines from five words" (let ((result (break-lines (list 30 30 30 30 30) 5 70))) (assert-equal 3 (len result)))) (deftest "single word per line when words are wide" (let ((result (break-lines (list 80 80 80) 5 90))) (assert-equal 3 (len result))))) ;; -------------------------------------------------------------------------- ;; Positioning ;; -------------------------------------------------------------------------- (defsuite "position-line" (deftest "single word at origin" (let ((result (position-line (list "hello") (list 50) 5 0 0))) (assert-equal 1 (len result)) (assert-equal 0 (get (first result) :x)) (assert-equal 0 (get (first result) :y)) (assert-equal "hello" (get (first result) :word)))) (deftest "two words spaced apart" (let ((result (position-line (list "hi" "there") (list 20 40) 5 0 10))) (assert-equal 2 (len result)) (assert-equal 0 (get (first result) :x)) (assert-equal 25 (get (nth result 1) :x)) (assert-equal 10 (get (nth result 1) :y)))) (deftest "custom x offset" (let ((result (position-line (list "word") (list 30) 5 10 0))) (assert-equal 10 (get (first result) :x))))) (defsuite "position-lines" (deftest "two lines stacked vertically" (let ((result (position-lines (list "a" "b" "c" "d") (list 10 10 10 10) (list (list 0 2) (list 2 4)) 5 20 0 0))) (assert-equal 2 (len result)) (assert-equal 0 (get (first (first result)) :y)) (assert-equal 20 (get (first (nth result 1)) :y))))) ;; -------------------------------------------------------------------------- ;; Hyphenation ;; -------------------------------------------------------------------------- (defsuite "hyphenation-trie" (deftest "build trie from patterns" (let ((trie (make-hyphenation-trie (list "hy1p" "he2n")))) (assert (dict? trie)) (assert (has-key? trie :children)))) (deftest "trie has expected structure" (let ((trie (make-hyphenation-trie (list "ab1c")))) (assert (has-key? (get trie :children) "a"))))) (defsuite "hyphenate-word" (deftest "word with no patterns returns single syllable" (let ((trie (make-hyphenation-trie (list)))) (assert-equal (list "xyz") (hyphenate-word trie "xyz")))) (deftest "simple hyphenation" (let ((trie (make-hyphenation-trie (list "hy1phen")))) (let ((syllables (hyphenate-word trie "hyphen"))) (assert (>= (len syllables) 1)))))) ;; -------------------------------------------------------------------------- ;; Integration: break + position ;; -------------------------------------------------------------------------- (defsuite "layout-integration" (deftest "break and position round-trip" (let ((widths (list 30 30 30 30)) (words (list "one" "two" "three" "four")) (space-width 5) (max-width 70) (line-height 20)) (let ((ranges (break-lines widths space-width max-width))) (let ((positioned (position-lines words widths ranges space-width line-height 0 0))) (assert (> (len positioned) 0)) (assert-equal 0 (get (first (first positioned)) :x)) (assert-equal 0 (get (first (first positioned)) :y)))))) (deftest "total height is lines × line-height" (let ((widths (list 80 80 80)) (space-width 5) (max-width 90) (line-height 20)) (let ((ranges (break-lines widths space-width max-width))) (assert-equal 3 (len ranges))))))