(ns unheard.interval-test (:require [unheard.interval :as sut] [hyperfiddle.rcf :refer [tests]])) (tests "Create interval with ratio boundaries and value" (let [iv (sut/ratio-interval 1/4 3/4 {:note :C, :velocity 64})] (.getNormStart iv) := 1/4 (.getNormEnd iv) := 3/4 (sut/interval-value iv) := {:note :C, :velocity 64})) (tests "Interval unique identifier" (let [iv (sut/ratio-interval 1/4 3/4 :test)] (.getUniqueIdentifier iv) := "[1/4,3/4]")) (tests "Interval comparison - start takes precedence" (let [iv1 (sut/ratio-interval 1/4 1/2 :first) iv2 (sut/ratio-interval 1/2 3/4 :second) iv3 (sut/ratio-interval 1/3 2/3 :third)] ;; iv1 starts before iv2 (.compareTo iv1 iv2) := -1 (.compareTo iv2 iv1) := 1 ;; iv1 starts before iv3 (.compareTo iv1 iv3) := -1 ;; iv3 starts after iv1 but before iv2 (.compareTo iv3 iv2) := -1)) (tests "Interval comparison - end is tiebreaker when starts are equal" (let [iv1 (sut/ratio-interval 1/4 1/2 :a) iv2 (sut/ratio-interval 1/4 3/4 :b)] ;; Same start, iv1 ends before iv2 (.compareTo iv1 iv2) := -1 (.compareTo iv2 iv1) := 1)) (tests "Interval comparison - equal intervals" (let [iv1 (sut/ratio-interval 1/4 1/2 :a) iv2 (sut/ratio-interval 1/4 1/2 :b)] ;; Same boundaries (values don't matter for comparison) (.compareTo iv1 iv2) := 0)) (tests "Find overlaps - basic cases" (let [coll (sut/create-ratio-interval-collection) iv1 (sut/ratio-interval 1/4 1/2 :first) iv2 (sut/ratio-interval 1/2 3/4 :second) iv3 (sut/ratio-interval 1/3 2/3 :third)] (.add coll iv1) (.add coll iv2) (.add coll iv3) ;; Query [1/3, 1/2] should overlap with all three (let [query (sut/ratio-interval 1/3 1/2 nil) overlaps (sut/find-overlaps coll query)] (count overlaps) := 3) ;; Query [5/8, 7/8] should overlap with iv2 and iv3 (let [query (sut/ratio-interval 5/8 7/8 nil) overlaps (sut/find-overlaps coll query)] (count overlaps) := 2))) (tests "Find overlaps - touching intervals at boundaries" (let [coll (sut/create-ratio-interval-collection) iv1 (sut/ratio-interval 0 1/4 :left) iv2 (sut/ratio-interval 1/4 1/2 :right)] (.add coll iv1) (.add coll iv2) ;; Query at exact boundary point should overlap both (let [query (sut/ratio-interval 1/4 1/4 :boundary) overlaps (sut/find-overlaps coll query)] (count overlaps) := 2 (set (map sut/interval-value overlaps)) := #{:left :right}))) (tests "Find overlaps - point interval" (let [coll (sut/create-ratio-interval-collection) iv1 (sut/ratio-interval 1/4 1/2 :wide) iv2 (sut/ratio-interval 1/3 1/3 :point)] (.add coll iv1) (.add coll iv2) ;; Point query inside wide interval (let [query (sut/ratio-interval 1/3 1/3 nil) overlaps (sut/find-overlaps coll query)] (count overlaps) := 2))) (tests "Find overlaps - no overlap" (let [coll (sut/create-ratio-interval-collection) iv1 (sut/ratio-interval 1/4 1/2 :first) iv2 (sut/ratio-interval 3/4 7/8 :second)] (.add coll iv1) (.add coll iv2) ;; Query between the two intervals (let [query (sut/ratio-interval 5/8 11/16 nil) overlaps (sut/find-overlaps coll query)] (count overlaps) := 0))) (tests "Interval with improper fractions" (let [iv (sut/ratio-interval 5/4 7/4 :improper)] (.getNormStart iv) := 5/4 (.getNormEnd iv) := 7/4 (sut/interval-value iv) := :improper)) (tests "Interval with negative ratios" (let [iv (sut/ratio-interval -3/4 -1/4 :negative)] (.getNormStart iv) := -3/4 (.getNormEnd iv) := -1/4)) (tests "Interval toString representation" (let [iv (sut/ratio-interval 1/4 3/4 {:data :test})] (str iv) := "[1/4, 3/4] -> {:data :test}")) (tests "Collection operations" (let [coll (sut/create-ratio-interval-collection) iv1 (sut/ratio-interval 1/4 1/2 :first) iv2 (sut/ratio-interval 1/2 3/4 :second)] (.add coll iv1) (.add coll iv2) (.size coll) := 2 ;; Can iterate over collection (set (map sut/interval-value coll)) := #{:first :second} ;; Can clear collection (.clear coll) (.size coll) := 0)) (tests "Value field can hold various types" (let [iv-map (sut/ratio-interval 0 1 {:key :value}) iv-keyword (sut/ratio-interval 0 1 :keyword) iv-string (sut/ratio-interval 0 1 "string") iv-number (sut/ratio-interval 0 1 42) iv-nil (sut/ratio-interval 0 1 nil)] (sut/interval-value iv-map) := {:key :value} (sut/interval-value iv-keyword) := :keyword (sut/interval-value iv-string) := "string" (sut/interval-value iv-number) := 42 (sut/interval-value iv-nil) := nil))