;; ==========================================================================
;; test-bytevectors.sx — Tests for bytevector primitives
;; ==========================================================================

;; --------------------------------------------------------------------------
;; make-bytevector / bytevector?
;; --------------------------------------------------------------------------

(defsuite
  "bv:create"
  (deftest
    "make-bytevector returns bytevector"
    (assert (bytevector? (make-bytevector 4))))
  (deftest
    "make-bytevector zeroes by default"
    (let
      ((bv (make-bytevector 3)))
      (assert
        (and
          (= (bytevector-u8-ref bv 0) 0)
          (= (bytevector-u8-ref bv 1) 0)
          (= (bytevector-u8-ref bv 2) 0)))))
  (deftest
    "make-bytevector with fill"
    (let
      ((bv (make-bytevector 3 42)))
      (assert
        (and
          (= (bytevector-u8-ref bv 0) 42)
          (= (bytevector-u8-ref bv 1) 42)
          (= (bytevector-u8-ref bv 2) 42)))))
  (deftest
    "make-bytevector length 0"
    (assert= (bytevector-length (make-bytevector 0)) 0))
  (deftest
    "bytevector? true for bytevector"
    (assert (bytevector? (make-bytevector 2))))
  (deftest
    "bytevector? false for string"
    (assert (not (bytevector? "hello"))))
  (deftest "bytevector? false for nil" (assert (not (bytevector? nil))))
  (deftest
    "bytevector? false for list"
    (assert (not (bytevector? (list 1 2 3))))))

;; --------------------------------------------------------------------------
;; bytevector-length / u8-ref / u8-set!
;; --------------------------------------------------------------------------

(defsuite
  "bv:access"
  (deftest
    "bytevector-length"
    (assert= (bytevector-length (make-bytevector 5)) 5))
  (deftest
    "u8-ref reads fill byte"
    (assert=
      (bytevector-u8-ref (make-bytevector 4 99) 2)
      99))
  (deftest
    "u8-set! mutates"
    (let
      ((bv (make-bytevector 3 0)))
      (bytevector-u8-set! bv 1 200)
      (assert= (bytevector-u8-ref bv 1) 200)))
  (deftest
    "u8-set! boundary byte 255"
    (let
      ((bv (make-bytevector 1 0)))
      (bytevector-u8-set! bv 0 255)
      (assert= (bytevector-u8-ref bv 0) 255)))
  (deftest
    "u8-set! byte 0"
    (let
      ((bv (make-bytevector 1 255)))
      (bytevector-u8-set! bv 0 0)
      (assert= (bytevector-u8-ref bv 0) 0))))

;; --------------------------------------------------------------------------
;; bytevector-copy
;; --------------------------------------------------------------------------

(defsuite
  "bv:copy"
  (deftest
    "copy produces equal content"
    (let
      ((bv (make-bytevector 3 7)))
      (let
        ((bv2 (bytevector-copy bv)))
        (assert
          (and
            (= (bytevector-u8-ref bv2 0) 7)
            (= (bytevector-u8-ref bv2 1) 7)
            (= (bytevector-u8-ref bv2 2) 7))))))
  (deftest
    "copy is independent"
    (let
      ((bv (make-bytevector 2 0)))
      (let
        ((bv2 (bytevector-copy bv)))
        (bytevector-u8-set! bv2 0 99)
        (assert= (bytevector-u8-ref bv 0) 0))))
  (deftest
    "copy with start"
    (let
      ((bv (list->bytevector (list 10 20 30 40))))
      (let
        ((bv2 (bytevector-copy bv 2)))
        (assert
          (and
            (= (bytevector-length bv2) 2)
            (= (bytevector-u8-ref bv2 0) 30))))))
  (deftest
    "copy with start and end"
    (let
      ((bv (list->bytevector (list 10 20 30 40))))
      (let
        ((bv2 (bytevector-copy bv 1 3)))
        (assert
          (and
            (= (bytevector-length bv2) 2)
            (= (bytevector-u8-ref bv2 0) 20)
            (= (bytevector-u8-ref bv2 1) 30)))))))

;; --------------------------------------------------------------------------
;; bytevector-copy!
;; --------------------------------------------------------------------------

(defsuite
  "bv:copy-bang"
  (deftest
    "copy! overwrites dst region"
    (let
      ((dst (make-bytevector 4 0)))
      (let
        ((src (list->bytevector (list 1 2 3))))
        (bytevector-copy! dst 1 src)
        (assert
          (and
            (= (bytevector-u8-ref dst 0) 0)
            (= (bytevector-u8-ref dst 1) 1)
            (= (bytevector-u8-ref dst 2) 2)
            (= (bytevector-u8-ref dst 3) 3))))))
  (deftest
    "copy! with src bounds"
    (let
      ((dst (make-bytevector 2 0)))
      (let
        ((src (list->bytevector (list 10 20 30 40))))
        (bytevector-copy! dst 0 src 1 3)
        (assert
          (and
            (= (bytevector-u8-ref dst 0) 20)
            (= (bytevector-u8-ref dst 1) 30)))))))

;; --------------------------------------------------------------------------
;; bytevector-append
;; --------------------------------------------------------------------------

(defsuite
  "bv:append"
  (deftest
    "append two bytevectors"
    (let
      ((bv (bytevector-append (list->bytevector (list 1 2)) (list->bytevector (list 3 4)))))
      (assert
        (and
          (= (bytevector-length bv) 4)
          (= (bytevector-u8-ref bv 0) 1)
          (= (bytevector-u8-ref bv 3) 4)))))
  (deftest
    "append three bytevectors"
    (let
      ((bv (bytevector-append (list->bytevector (list 1)) (list->bytevector (list 2)) (list->bytevector (list 3)))))
      (assert= (bytevector-length bv) 3)))
  (deftest
    "append empty"
    (assert=
      (bytevector-length
        (bytevector-append
          (make-bytevector 0)
          (make-bytevector 0)))
      0)))

;; --------------------------------------------------------------------------
;; list->bytevector / bytevector->list
;; --------------------------------------------------------------------------

(defsuite
  "bv:conversion"
  (deftest
    "list->bytevector roundtrip"
    (let
      ((lst (list 10 20 30)))
      (assert= (bytevector->list (list->bytevector lst)) lst)))
  (deftest
    "list->bytevector empty"
    (assert= (bytevector-length (list->bytevector nil)) 0))
  (deftest
    "bytevector->list from make-bytevector"
    (let
      ((lst (bytevector->list (make-bytevector 3 5))))
      (assert= lst (list 5 5 5)))))

;; --------------------------------------------------------------------------
;; utf8 roundtrip
;; --------------------------------------------------------------------------

(defsuite
  "bv:utf8"
  (deftest
    "string->utf8 returns bytevector"
    (assert (bytevector? (string->utf8 "hello"))))
  (deftest
    "string->utf8 length"
    (assert= (bytevector-length (string->utf8 "abc")) 3))
  (deftest
    "utf8->string roundtrip"
    (assert= (utf8->string (string->utf8 "hello")) "hello"))
  (deftest
    "utf8->string with slice"
    (let
      ((bv (string->utf8 "hello")))
      (assert= (utf8->string bv 1 4) "ell")))
  (deftest
    "string->utf8 with start"
    (assert= (utf8->string (string->utf8 "hello" 2)) "llo"))
  (deftest
    "string->utf8 with start and end"
    (assert=
      (utf8->string (string->utf8 "hello" 1 4))
      "ell"))
  (deftest
    "empty string round-trips"
    (assert= (utf8->string (string->utf8 "")) "")))
