🚧 WIP Step 8, simplify and clean up tests

This commit is contained in:
Miguel de Benito Delgado 2026-09-22 16:00:02 +02:00
parent 44dfb47c98
commit c16233f841
5 changed files with 235 additions and 382 deletions

View File

@ -7,48 +7,21 @@
(ns frontend-tests.benches.boolean-fixture-test
(:require
[app.common.types.path :as path]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[benches.render-wasm.scenes.builder :as b :include-macros true]
[benches.render-wasm.scenes.common :as common]
[cljs.test :as t :include-macros true]))
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
(defn- failure-data
[f]
(try
(f)
nil
(catch :default cause
(ex-data cause))))
(defn- selrect
[shape]
[(:x (:selrect shape)) (:y (:selrect shape))
(:width (:selrect shape)) (:height (:selrect shape))])
(defn- children-of
[instance id]
(mapv (:objects instance) (get-in instance [:objects id :shapes])))
(defn- run-scope
"Runs `f` inside a raw scope and returns the scope state, so tests can
inspect partial builds without calling finish!."
[params f]
(let [state (b/start params)]
(binding [b/*state* state
b/*defaults* {}]
(f))
state))
(t/deftest difference-of-overlapping-rects-derives-the-l-shape
(t/deftest difference-of-overlapping-rects-keeps-hierarchy-and-bounds
(let [red {:fill-color "#ff0000" :fill-opacity 1}
blue {:fill-color "#0000ff" :fill-opacity 1}
instance (b/fixture {:seed 1}
(b/bool :l-shape {:bool-type :difference :name "L"}
(b/rect {:x 0 :y 0 :width 100 :height 100 :fills [red]})
(b/rect {:x 50 :y 50 :width 100 :height 100 :fills [blue]})))
bool (first (children-of instance uuid/zero))
children (children-of instance (:id bool))]
bool (first (helpers/children-of instance uuid/zero))
children (helpers/children-of instance (:id bool))]
(t/is (= :bool (:type bool)))
(t/is (= :difference (:bool-type bool)))
(t/is (= (:id bool) (common/ref-id instance :l-shape)))
@ -58,25 +31,18 @@
[(:parent-id (first children)) (:parent-id (second children))]))
(t/is (= [uuid/zero uuid/zero]
[(:frame-id (first children)) (:frame-id (second children))]))
(t/is (= [0 0 100 100] (selrect bool)))
(t/is (= (:selrect bool) (path/calc-selrect (:content bool))))
(t/is (= 4 (count (:points bool))))
(t/is (= #{[0 0] [100 0] [100 50] [50 50] [50 100] [0 100]}
(set (map (juxt :x :y) (path/get-points (:content bool))))))))
(t/is (= [0 0 100 100] (helpers/selrect bool)))))
(t/deftest union-intersection-and-exclude-derive-canonical-bounds
(doseq [[bool-type expected] [[:union [0 0 150 150]]
[:intersection [50 50 50 50]]
[:exclude [0 0 150 150]]]]
(let [instance (b/fixture {:seed 1}
(b/bool {:bool-type bool-type}
(b/rect {:x 0 :y 0 :width 100 :height 100})
(b/rect {:x 50 :y 50 :width 100 :height 100})))
bool (first (children-of instance uuid/zero))]
(t/is (= bool-type (:bool-type bool)))
(t/is (= expected (selrect bool)) (str bool-type)))))
(t/deftest union-derives-canonical-bounds
(let [instance (b/fixture {:seed 1}
(b/bool {:bool-type :union}
(b/rect {:x 0 :y 0 :width 100 :height 100})
(b/rect {:x 50 :y 50 :width 100 :height 100})))
bool (first (helpers/children-of instance uuid/zero))]
(t/is (= :union (:bool-type bool)))
(t/is (= [0 0 150 150] (helpers/selrect bool)))))
(t/deftest operation-picks-the-canonical-style-head
(t/deftest difference-picks-the-first-child-as-style-head
(let [red {:fill-color "#ff0000" :fill-opacity 1}
blue {:fill-color "#0000ff" :fill-opacity 1}
blur {:id (uuid/custom 3 3) :type :layer-blur :value 4 :hidden false}
@ -86,18 +52,9 @@
:fills [red] :blur blur})
(b/rect {:x 5 :y 0 :width 10 :height 10
:fills [blue]})))
union (b/fixture {:seed 1}
(b/bool {:bool-type :union}
(b/rect {:x 0 :y 0 :width 10 :height 10
:fills [red] :blur blur})
(b/rect {:x 5 :y 0 :width 10 :height 10
:fills [blue]})))
diff-bool (first (children-of difference uuid/zero))
union-bool (first (children-of union uuid/zero))]
diff-bool (first (helpers/children-of difference uuid/zero))]
(t/is (= [red] (:fills diff-bool)))
(t/is (= blur (:blur diff-bool)))
(t/is (= [blue] (:fills union-bool)))
(t/is (nil? (:blur union-bool)))))
(t/is (= blur (:blur diff-bool)))))
(t/deftest supplied-styles-override-inherited
(let [instance (b/fixture {:seed 1}
@ -106,7 +63,7 @@
:fills [{:fill-color "#ff0000" :fill-opacity 1}]})
(b/rect {:x 5 :y 0 :width 10 :height 10
:fills [{:fill-color "#0000ff" :fill-opacity 1}]})))
bool (first (children-of instance uuid/zero))]
bool (first (helpers/children-of instance uuid/zero))]
(t/is (= [] (:fills bool)))))
(t/deftest nil-styles-do-not-count-as-supplied
@ -116,16 +73,16 @@
(b/bool {:bool-type :union :fills nil}
(b/rect {:x 0 :y 0 :width 10 :height 10 :fills [red]})
(b/rect {:x 5 :y 0 :width 10 :height 10 :fills [blue]})))
bool (first (children-of instance uuid/zero))]
bool (first (helpers/children-of instance uuid/zero))]
(t/is (= [blue] (:fills bool)))))
(t/deftest single-child-bool-uses-the-child-path
(let [instance (b/fixture {:seed 1}
(b/bool {:bool-type :difference}
(b/rect {:x 10 :y 20 :width 30 :height 40})))
bool (first (children-of instance uuid/zero))]
bool (first (helpers/children-of instance uuid/zero))]
(t/is (= 1 (count (:shapes bool))))
(t/is (= [10 20 30 40] (selrect bool)))
(t/is (= [10 20 30 40] (helpers/selrect bool)))
(t/is (= (:selrect bool) (path/calc-selrect (:content bool))))))
(t/deftest nested-bools-finalize-inside-out
@ -135,11 +92,11 @@
(b/rect {:x 0 :y 0 :width 100 :height 100})
(b/rect {:x 50 :y 50 :width 100 :height 100}))
(b/rect {:x 200 :y 0 :width 50 :height 50})))
outer (first (children-of instance uuid/zero))
inner (first (children-of instance (:id outer)))]
outer (first (helpers/children-of instance uuid/zero))
inner (first (helpers/children-of instance (:id outer)))]
(t/is (= :bool (:type inner)))
(t/is (= [0 0 100 100] (selrect inner)))
(t/is (= [0 0 250 100] (selrect outer)))
(t/is (= [0 0 100 100] (helpers/selrect inner)))
(t/is (= [0 0 250 100] (helpers/selrect outer)))
(t/is (= (:selrect outer) (path/calc-selrect (:content outer))))))
(t/deftest bool-inside-frame-keeps-frame-ids
@ -148,9 +105,9 @@
(b/bool {:bool-type :union}
(b/rect {:x 10 :y 10 :width 10 :height 10})
(b/rect {:x 30 :y 10 :width 10 :height 10}))))
frame (first (children-of instance uuid/zero))
bool (first (children-of instance (:id frame)))
rects (children-of instance (:id bool))]
frame (first (helpers/children-of instance uuid/zero))
bool (first (helpers/children-of instance (:id frame)))
rects (helpers/children-of instance (:id bool))]
(t/is (= (:id frame) (:parent-id bool)))
(t/is (= (:id frame) (:frame-id bool)))
(t/is (= [(:id frame) (:id frame)] (mapv :frame-id rects)))
@ -162,30 +119,30 @@
(b/bool {:bool-type :union}
(b/rect {:x 10 :y 20 :width 30 :height 40})
(b/rect {:x 50 :y 80 :width 10 :height 10}))))
group (first (children-of instance uuid/zero))
bool (first (children-of instance (:id group)))]
group (first (helpers/children-of instance uuid/zero))
bool (first (helpers/children-of instance (:id group)))]
(t/is (= :bool (:type bool)))
(t/is (= [10 20 50 70] (selrect group)))
(t/is (= [10 20 50 70] (helpers/selrect group)))
(t/is (= uuid/zero (:frame-id bool)))))
(t/deftest empty-bool-is-rejected
(let [data (failure-data #(b/fixture {:seed 1} (b/bool {:bool-type :union})))]
(let [data (helpers/failure-data #(b/fixture {:seed 1} (b/bool {:bool-type :union})))]
(t/is (= ::b/empty-bool (:type data)))))
(t/deftest caught-empty-bool-still-fails-finish
(let [state (run-scope {:seed 1}
(fn []
(try
(b/bool {:bool-type :union})
(catch :default _ nil))))]
(let [state (helpers/run-scope {:seed 1}
(fn []
(try
(b/bool {:bool-type :union})
(catch :default _ nil))))]
(t/is (= ::b/unfinished-container
(:type (failure-data #(b/finish! state)))))))
(:type (helpers/failure-data #(b/finish! state)))))))
(t/deftest bool-geometry-attrs-are-rejected
(doseq [attrs [{:bool-type :union :x 1}
{:bool-type :union :selrect {:x 0 :y 0 :width 1 :height 1}}
{:bool-type :union :content []}]]
(let [data (failure-data #(b/fixture {:seed 1} (b/bool attrs (b/rect))))]
(let [data (helpers/failure-data #(b/fixture {:seed 1} (b/bool attrs (b/rect))))]
(t/is (= ::b/bool-geometry (:type data)) (str attrs))
(t/is (contains? #{:x :selrect :content} (:key data)) (str attrs)))))
@ -195,19 +152,19 @@
{:bool-type :union :transform-inverse "m"}
{:bool-type :union :flip-x true}
{:bool-type :union :flip-y true}]]
(let [data (failure-data #(b/fixture {:seed 1} (b/bool attrs (b/rect))))]
(let [data (helpers/failure-data #(b/fixture {:seed 1} (b/bool attrs (b/rect))))]
(t/is (= ::b/bool-transform (:type data)) (str attrs)))))
(t/deftest bool-type-is-required-and-validated
(t/is (= ::b/bool-type
(:type (failure-data #(b/fixture {:seed 1} (b/bool {:name "B"} (b/rect)))))))
(let [data (failure-data #(b/fixture {:seed 1} (b/bool {:bool-type :merge} (b/rect))))]
(:type (helpers/failure-data #(b/fixture {:seed 1} (b/bool {:name "B"} (b/rect)))))))
(let [data (helpers/failure-data #(b/fixture {:seed 1} (b/bool {:bool-type :merge} (b/rect))))]
(t/is (= ::b/bool-type (:type data)))
(t/is (= :merge (:bool-type data)))
(t/is (= cts/bool-types (:supported data)))))
(t/is (= #{:union :difference :intersection :exclude} (set (:supported data))))))
(t/deftest frame-child-is-rejected
(let [data (failure-data
(let [data (helpers/failure-data
#(b/fixture {:seed 1}
(b/bool {:bool-type :union}
(b/frame {:x 0 :y 0 :width 10 :height 10}
@ -226,17 +183,17 @@
(b/bool {:bool-type :difference}
(b/rect {:x 0 :y 0 :width 100 :height 100})
(b/rect {:x 50 :y 50 :width 100 :height 100})))
bool (first (children-of instance uuid/zero))]
(t/is (= [0 0 100 100] (selrect bool))))
bool (first (helpers/children-of instance uuid/zero))]
(t/is (= [0 0 100 100] (helpers/selrect bool))))
(finally
(set! path/wasm:calc-bool-content original)))))
(t/deftest non-map-bool-attrs-are-rejected
(t/is (= ::b/invalid-attrs
(:type (failure-data #(b/fixture {:seed 1} (b/bool :hero (b/rect)))))))
(let [state (run-scope {:seed 1}
(fn []
(try
(b/bool :hero (b/rect))
(catch :default _ nil))))]
(:type (helpers/failure-data #(b/fixture {:seed 1} (b/bool :hero (b/rect)))))))
(let [state (helpers/run-scope {:seed 1}
(fn []
(try
(b/bool :hero (b/rect))
(catch :default _ nil))))]
(t/is (= 1 (count (get-in @state [:objects uuid/zero :shapes]))))))

View File

@ -10,26 +10,13 @@
[benches.render-wasm.scenes.builder :as b :include-macros true]
[benches.render-wasm.scenes.common :as common]
[benches.render-wasm.scenes.rects :as rects]
[cljs.test :as t :include-macros true]))
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
(defn- build
([] (build {}))
([params] (rects/build (merge {:seed 42 :count 5} params))))
(defn- rect-shapes
"Rectangles of a fixture instance in creation order."
[instance]
(mapv (:objects instance)
(get-in instance [:objects uuid/zero :shapes])))
(defn- failure-data
[f]
(try
(f)
nil
(catch :default cause
(ex-data cause))))
(defn- add-tile
"Helper defined outside a fixture scope, called from inside one."
[index]
@ -41,13 +28,13 @@
(t/deftest rectangle-workload-keeps-default-count
(let [instance (rects/build {:seed 42})
rects (rect-shapes instance)]
rects (helpers/root-shapes instance)]
(t/is (= 1000 (count rects)))
(t/is (< 1 (count (distinct (map :x rects)))))))
(t/deftest default-workload-is-canonical
(let [instance (build {:count 25})
rects (rect-shapes instance)]
rects (helpers/root-shapes instance)]
(t/is (= 25 (count rects)))
(t/is (every? #(= :rect (:type %)) rects))
(t/is (every? (fn [rect]
@ -65,7 +52,7 @@
(t/deftest geometry-is-page-relative-and-in-range
(let [params {:seed 7 :count 40 :width 800 :height 600 :min-size 5 :max-size 15}
instance (build params)
rects (rect-shapes instance)]
rects (helpers/root-shapes instance)]
(t/is (every? #(<= 0 (:x %) 800) rects))
(t/is (every? #(<= 0 (:y %) 600) rects))
(t/is (every? #(<= 5 (:width %) 15) rects))
@ -79,7 +66,7 @@
(b/rect :hero {})
(b/rect [:tile 1] {}))
refs (:refs instance)
children (get-in instance [:objects uuid/zero :shapes])]
children (helpers/child-ids instance uuid/zero)]
(t/is (= #{:hero [:tile 1]} (set (keys refs))))
(t/is (= (get refs :hero) (second children)))
(t/is (= (get refs [:tile 1]) (nth children 2)))))
@ -88,7 +75,7 @@
(let [instance (b/fixture {:seed 1
:defaults {:rect {:x (b/gen-int 0 100)}}}
(b/rect {:x 7 :fills []}))
rect (first (rect-shapes instance))]
rect (first (helpers/root-shapes instance))]
(t/is (= 7 (:x rect)))
(t/is (= [] (:fills rect)))))
@ -100,7 +87,7 @@
(add-tile index))
(b/rect :last {:x 99}))
refs (:refs instance)
rects (rect-shapes instance)]
rects (helpers/root-shapes instance)]
(t/is (= 5 (count rects)))
(t/is (= [0 10 20]
(mapv #(:x (get-in instance [:objects (get refs [:tile %])]))
@ -108,7 +95,7 @@
(t/is (= 99 (:x (get-in instance [:objects (get refs :last)]))))))
(t/deftest duplicate-labels-are-rejected
(let [data (failure-data
(let [data (helpers/failure-data
#(b/fixture {:seed 1}
(b/rect :dup {})
(b/rect :dup {})))]
@ -117,17 +104,17 @@
(t/deftest failed-builds-leak-no-state
(let [clean (build)]
(failure-data #(b/fixture {:seed 99}
(b/rect :dup {})
(b/rect :dup {})))
(helpers/failure-data #(b/fixture {:seed 99}
(b/rect :dup {})
(b/rect :dup {})))
(t/is (= clean (build)))))
(t/deftest rect-outside-scope-is-rejected
(let [data (failure-data #(b/rect))]
(let [data (helpers/failure-data #(b/rect))]
(t/is (= ::b/outside-scope (:type data)))))
(t/deftest nested-scopes-are-rejected
(let [data (failure-data
(let [data (helpers/failure-data
#(b/fixture {:seed 1}
(b/fixture {:seed 2}
(b/rect))))]
@ -135,17 +122,17 @@
(t/deftest invalid-seeds-are-rejected
(t/is (= ::b/invalid-seed
(:type (failure-data #(b/fixture {} (b/rect))))))
(:type (helpers/failure-data #(b/fixture {} (b/rect))))))
(t/is (= ::b/invalid-seed
(:type (failure-data #(b/fixture {:seed -1} (b/rect))))))
(:type (helpers/failure-data #(b/fixture {:seed -1} (b/rect))))))
(t/is (= ::b/invalid-seed
(:type (failure-data #(b/fixture {:seed 4294967296} (b/rect)))))))
(:type (helpers/failure-data #(b/fixture {:seed 4294967296} (b/rect)))))))
(t/deftest runtime-attrs-maps-are-resolved
(let [attrs {:x 11}
instance (b/fixture {:seed 1}
(b/rect attrs))
rect (first (rect-shapes instance))]
rect (first (helpers/root-shapes instance))]
(t/is (= 11 (:x rect)))))
(t/deftest single-label-form-is-supported
@ -153,7 +140,7 @@
(b/rect :solo))
id (get (:refs instance) :solo)]
(t/is (uuid? id))
(t/is (= [id] (get-in instance [:objects uuid/zero :shapes])))))
(t/is (= [id] (helpers/child-ids instance uuid/zero)))))
(t/deftest built-instance-feeds-the-snapshot-api
(let [instance (b/fixture {:seed 1}
@ -163,14 +150,14 @@
(t/is (= [uuid/zero hero-id] (mapv :id order)))))
(t/deftest invalid-labels-are-rejected
(let [data (failure-data #(b/fixture {:seed 1} (b/rect "hero" {})))]
(let [data (helpers/failure-data #(b/fixture {:seed 1} (b/rect "hero" {})))]
(t/is (= ::b/invalid-label (:type data)))))
(t/deftest protected-keys-cannot-be-overridden
(let [other (uuid/custom 9 9)
instance (b/fixture {:seed 1}
(b/rect {:type :circle :id other :parent-id other}))
rect (first (rect-shapes instance))]
rect (first (helpers/root-shapes instance))]
(t/is (= :rect (:type rect)))
(t/is (not= other (:id rect)))
(t/is (= uuid/zero (:parent-id rect)))))
@ -189,61 +176,47 @@
(let [fill {:fill-color "#ffffff" :fill-opacity 1}
instance (b/fixture {:seed 1}
(b/rect {:fills [fill]}))
rect (first (rect-shapes instance))]
rect (first (helpers/root-shapes instance))]
(t/is (= [fill] (:fills rect)))))
(t/deftest generator-helpers-produce-expected-values
(let [instance (b/fixture {:seed 3
:defaults {:rect {:opacity (b/gen-float 0.5 0.6)
:x (b/gen-one-of [4 5])
:r1 (b/gen-int 3 4)
:r1 (b/gen-int 3 5)
:fills (b/gen-vector (b/gen-fill))}}}
(b/rect))
rect (first (rect-shapes instance))]
rect (first (helpers/root-shapes instance))]
(t/is (<= 0.5 (:opacity rect) 0.6))
(t/is (contains? #{4 5} (:x rect)))
(t/is (= 3 (:r1 rect)))
(t/is (<= 3 (:r1 rect) 4))
(t/is (re-matches #"^#[0-9a-f]{6}$" (:fill-color (first (:fills rect)))))))
(t/deftest empty-one-of-is-rejected
(let [data (failure-data #(b/gen-one-of []))]
(let [data (helpers/failure-data #(b/gen-one-of []))]
(t/is (= ::b/no-values (:type data)))))
(defn- circle-shapes
"Circles of a fixture instance in creation order."
[instance]
(mapv (:objects instance)
(get-in instance [:objects uuid/zero :shapes])))
(t/deftest circle-creates-ellipse-type
(let [instance (b/fixture {:seed 1}
(b/circle {:x 10 :y 20 :width 30 :height 40}))
circle (first (circle-shapes instance))]
circle (first (helpers/root-shapes instance))]
(t/is (= :circle (:type circle)))
(t/is (= [10 20 30 40] [(:x circle) (:y circle) (:width circle) (:height circle)]))
(t/is (= 1 (count (get-in instance [:objects uuid/zero :shapes]))))))
(t/is (= 1 (count (helpers/child-ids instance uuid/zero))))))
(t/deftest circle-uses-circle-defaults
(let [instance (b/fixture {:seed 3
:defaults {:circle {:x (b/gen-int 10 11)}}}
:defaults {:circle {:x (b/gen-int 10 12)}}}
(b/circle))
circle (first (circle-shapes instance))]
circle (first (helpers/root-shapes instance))]
(t/is (contains? #{10 11} (:x circle)))))
(t/deftest circle-ignores-rect-defaults
(let [instance (b/fixture {:seed 1
:defaults {:rect {:x 999}}}
(b/circle))
circle (first (circle-shapes instance))]
(t/is (= 0 (:x circle)))))
(t/deftest circle-attrs-override-generated-values
(let [instance (b/fixture {:seed 1
:defaults {:circle {:x (b/gen-int 0 100)}}}
(b/circle {:x 7 :fills []}))
circle (first (circle-shapes instance))]
(t/is (= 7 (:x circle)))
(t/is (= [] (:fills circle)))))
circle (first (helpers/root-shapes instance))]
(t/is (not= 999 (:x circle)))))
(t/deftest circle-geometry-is-page-relative-and-in-range
(let [instance (b/fixture {:seed 7
@ -253,101 +226,16 @@
:height (b/gen-int 5 15)}}}
(doseq [_ (range 40)]
(b/circle)))
circles (circle-shapes instance)]
circles (helpers/root-shapes instance)]
(t/is (= 40 (count circles)))
(t/is (every? #(<= 0 (:x %) 800) circles))
(t/is (every? #(<= 0 (:y %) 600) circles))
(t/is (every? #(<= 5 (:width %) 15) circles))
(t/is (every? #(<= 5 (:height %) 15) circles))))
(t/deftest circle-labels-are-instance-local
(let [instance (b/fixture {:seed 1}
(b/circle)
(b/circle :hero {})
(b/circle [:tile 1] {}))
refs (:refs instance)
children (get-in instance [:objects uuid/zero :shapes])]
(t/is (= #{:hero [:tile 1]} (set (keys refs))))
(t/is (= (get refs :hero) (second children)))
(t/is (= (get refs [:tile 1]) (nth children 2)))))
(t/deftest circle-single-label-form-is-supported
(let [instance (b/fixture {:seed 1}
(b/circle :solo))
id (get (:refs instance) :solo)]
(t/is (uuid? id))
(t/is (= [id] (get-in instance [:objects uuid/zero :shapes])))))
(t/deftest circle-runtime-attrs-maps-are-resolved
(let [attrs {:x 11}
instance (b/fixture {:seed 1}
(b/circle attrs))
circle (first (circle-shapes instance))]
(t/is (= 11 (:x circle)))))
(t/deftest circle-duplicate-labels-are-rejected
(let [data (failure-data
#(b/fixture {:seed 1}
(b/circle :dup {})
(b/circle :dup {})))]
(t/is (= ::b/duplicate-label (:type data)))
(t/is (= :dup (:label data)))))
(t/deftest circle-invalid-labels-are-rejected
(let [data (failure-data #(b/fixture {:seed 1} (b/circle "hero" {})))]
(t/is (= ::b/invalid-label (:type data)))))
(t/deftest circle-outside-scope-is-rejected
(let [data (failure-data #(b/circle))]
(t/is (= ::b/outside-scope (:type data)))))
(t/deftest circle-protected-keys-cannot-be-overridden
(let [other (uuid/custom 9 9)
instance (b/fixture {:seed 1}
(b/circle {:type :rect :id other :parent-id other}))
circle (first (circle-shapes instance))]
(t/is (= :circle (:type circle)))
(t/is (not= other (:id circle)))
(t/is (= uuid/zero (:parent-id circle)))))
(t/deftest circle-built-instance-feeds-the-snapshot-api
(let [instance (b/fixture {:seed 1}
(b/circle :hero {}))
order (common/upload-order instance)
hero-id (common/ref-id instance :hero)]
(t/is (= [uuid/zero hero-id] (mapv :id order)))))
(t/deftest circle-repeatable-builds-are-identical
(let [build (fn [seed]
(b/fixture {:seed seed
:defaults {:circle {:x (b/gen-int 0 100)}}}
(b/circle)
(b/circle)))]
(t/is (= (build 42) (build 42)))
(t/is (not= (build 42) (build 43)))))
(t/deftest circle-ordinary-forms-work-inside-the-scope
(let [instance (b/fixture {:seed 1}
(let [first-id (b/circle {:x 1})]
(t/is (uuid? first-id)))
(doseq [index (range 3)]
(b/circle [:ring index] {:x (* 10 index)}))
(b/circle :last {:x 99}))
refs (:refs instance)
circles (circle-shapes instance)]
(t/is (= 5 (count circles)))
(t/is (= [0 10 20]
(mapv #(:x (get-in instance [:objects (get refs [:ring %])]))
(range 3))))
(t/is (= 99 (:x (get-in instance [:objects (get refs :last)]))))))
(t/deftest circle-preserves-radius-attrs
(let [instance (b/fixture {:seed 1}
(b/circle {:x 0 :y 0 :width 20 :height 20 :rx 5 :ry 7}))
circle (first (circle-shapes instance))]
circle (first (helpers/root-shapes instance))]
(t/is (= [5 7] [(:rx circle) (:ry circle)]))))
(t/deftest circle-failed-builds-leak-no-state
(let [clean (b/fixture {:seed 42 :defaults {:circle {:x (b/gen-int 0 100)}}} (b/circle) (b/circle))]
(failure-data #(b/fixture {:seed 99} (b/circle :dup {}) (b/circle :dup {})))
(t/is (= clean (b/fixture {:seed 42 :defaults {:circle {:x (b/gen-int 0 100)}}} (b/circle) (b/circle))))))

View File

@ -8,46 +8,18 @@
(:require
[app.common.uuid :as uuid]
[benches.render-wasm.scenes.builder :as builder :include-macros true]
[benches.render-wasm.scenes.common :as common]
[cljs.test :as t :include-macros true]))
(defn- failure-data
[f]
(try
(f)
nil
(catch :default cause
(ex-data cause))))
(defn- child-ids
"Ids listed in the :shapes vector of `id`."
[instance id]
(get-in instance [:objects id :shapes]))
(defn- children-of
"Child shapes of `id` in order."
[instance id]
(mapv (:objects instance) (child-ids instance id)))
(defn- run-scope
"Runs `f` inside a raw scope and returns the scope state, so tests can
inspect partial builds without calling finish!."
[params f]
(let [state (builder/start params)]
(binding [builder/*state* state
builder/*defaults* {}]
(f))
state))
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
(t/deftest frame-attaches-children-in-order
(let [instance (builder/fixture {:seed 1}
(builder/frame {:x 0 :y 0 :width 100 :height 100 :name "F"}
(builder/rect {:x 10 :y 10 :width 5 :height 5})
(builder/rect {:x 20 :y 20 :width 5 :height 5})))
frame (first (children-of instance uuid/zero))
rects (children-of instance (:id frame))]
frame (first (helpers/children-of instance uuid/zero))
rects (helpers/children-of instance (:id frame))]
(t/is (= :frame (:type frame)))
(t/is (= [(:id frame)] (child-ids instance uuid/zero)))
(t/is (= [(:id frame)] (helpers/child-ids instance uuid/zero)))
(t/is (= 2 (count rects)))
(t/is (every? #(= (:id frame) (:parent-id %)) rects))
(t/is (every? #(= (:id frame) (:frame-id %)) rects))
@ -60,9 +32,9 @@
(builder/frame {:x 0 :y 0 :width 100 :height 100 :name "Outer"}
(builder/frame {:x 10 :y 10 :width 50 :height 50 :name "Inner"}
(builder/rect {:x 20 :y 20 :width 5 :height 5}))))
outer (first (children-of instance uuid/zero))
inner (first (children-of instance (:id outer)))
rect (first (children-of instance (:id inner)))]
outer (first (helpers/children-of instance uuid/zero))
inner (first (helpers/children-of instance (:id outer)))
rect (first (helpers/children-of instance (:id inner)))]
(t/is (= uuid/zero (:frame-id outer)))
(t/is (= (:id outer) (:parent-id inner)))
(t/is (= (:id outer) (:frame-id inner)))
@ -75,14 +47,11 @@
(builder/group {}
(builder/rect {:x 10 :y 20 :width 30 :height 40})
(builder/rect {:x 50 :y 80 :width 10 :height 10})))
group (first (children-of instance uuid/zero))
rects (children-of instance (:id group))]
group (first (helpers/children-of instance uuid/zero))
rects (helpers/children-of instance (:id group))]
(t/is (= :group (:type group)))
(t/is (= [10 20 50 70]
[(:x group) (:y group) (:width group) (:height group)]))
(t/is (= [10 20 50 70]
[(:x (:selrect group)) (:y (:selrect group))
(:width (:selrect group)) (:height (:selrect group))]))
(t/is (every? #(= uuid/zero (:frame-id %)) rects))
(t/is (every? #(= (:id group) (:parent-id %)) rects))))
@ -92,8 +61,8 @@
(builder/rect {:x 0 :y 0 :width 10 :height 10})
(builder/group {:name "Inner"}
(builder/rect {:x 100 :y 100 :width 10 :height 10}))))
outer (first (children-of instance uuid/zero))
inner (second (children-of instance (:id outer)))]
outer (first (helpers/children-of instance uuid/zero))
inner (second (helpers/children-of instance (:id outer)))]
(t/is (= :group (:type inner)))
(t/is (= [100 100 10 10]
[(:x inner) (:y inner) (:width inner) (:height inner)]))
@ -105,9 +74,9 @@
(builder/frame {:x 0 :y 0 :width 200 :height 200}
(builder/group {}
(builder/rect {:x 10 :y 10 :width 10 :height 10}))))
frame (first (children-of instance uuid/zero))
group (first (children-of instance (:id frame)))
rect (first (children-of instance (:id group)))]
frame (first (helpers/children-of instance uuid/zero))
group (first (helpers/children-of instance (:id frame)))
rect (first (helpers/children-of instance (:id group)))]
(t/is (= (:id frame) (:frame-id group)))
(t/is (= (:id frame) (:frame-id rect)))
(t/is (= (:id group) (:parent-id rect)))))
@ -117,7 +86,7 @@
(builder/group {:masked-group true}
(builder/rect {:x 10 :y 20 :width 30 :height 40})
(builder/rect {:x 0 :y 0 :width 500 :height 500})))
group (first (children-of instance uuid/zero))]
group (first (helpers/children-of instance uuid/zero))]
(t/is (true? (:masked-group group)))
(t/is (= [10 20 30 40]
[(:x group) (:y group) (:width group) (:height group)]))))
@ -129,25 +98,16 @@
(builder/rect))))
refs (:refs instance)]
(t/is (= #{:hero [:g 1]} (set (keys refs))))
(t/is (= (get refs :hero) (first (child-ids instance uuid/zero))))
(t/is (= (get refs [:g 1]) (first (child-ids instance (get refs :hero)))))
(t/is (= 1 (count (child-ids instance uuid/zero))))))
(t/deftest nested-order-follows-construction
(let [instance (builder/fixture {:seed 1}
(builder/frame {:x 0 :y 0 :width 100 :height 100}
(builder/rect)
(builder/group {}
(builder/rect))))
order (mapv :type (common/upload-order instance))]
(t/is (= [:frame :frame :rect :group :rect] order))))
(t/is (= (get refs :hero) (first (helpers/child-ids instance uuid/zero))))
(t/is (= (get refs [:g 1]) (first (helpers/child-ids instance (get refs :hero)))))
(t/is (= 1 (count (helpers/child-ids instance uuid/zero))))))
(t/deftest empty-group-is-rejected
(let [data (failure-data #(builder/fixture {:seed 1} (builder/group {})))]
(let [data (helpers/failure-data #(builder/fixture {:seed 1} (builder/group {})))]
(t/is (= ::builder/empty-group (:type data)))))
(t/deftest group-geometry-attrs-are-rejected
(let [data (failure-data
(let [data (helpers/failure-data
#(builder/fixture {:seed 1}
(builder/group {:x 1}
(builder/rect))))]
@ -155,7 +115,7 @@
(t/is (= :x (:key data)))))
(t/deftest frame-requires-bounds
(let [data (failure-data
(let [data (helpers/failure-data
#(builder/fixture {:seed 1}
(builder/frame {:name "F"}
(builder/rect))))]
@ -166,24 +126,24 @@
instance (builder/fixture {:seed 1}
(builder/frame frame-attrs
(builder/rect)))
frame (first (children-of instance uuid/zero))]
frame (first (helpers/children-of instance uuid/zero))]
(t/is (= :frame (:type frame)))
(t/is (= 10 (:width frame)))))
(t/deftest non-map-attrs-are-rejected
(t/is (= ::builder/invalid-attrs
(:type (failure-data
(:type (helpers/failure-data
#(builder/fixture {:seed 1}
(builder/frame :hero (builder/rect)))))))
(let [state (run-scope {:seed 1}
(fn []
(try
(builder/frame :hero (builder/rect))
(catch :default _ nil))))]
(t/is (= 1 (count (child-ids @state uuid/zero))))))
(let [state (helpers/run-scope {:seed 1}
(fn []
(try
(builder/frame :hero (builder/rect))
(catch :default _ nil))))]
(t/is (= 1 (count (helpers/child-ids @state uuid/zero))))))
(t/deftest non-numeric-frame-bounds-are-rejected
(let [data (failure-data
(let [data (helpers/failure-data
#(builder/fixture {:seed 1}
(builder/frame {:x 0 :y 0 :width "100" :height 10}
(builder/rect))))]
@ -191,7 +151,7 @@
(t/is (= :width (:invalid data)))))
(t/deftest duplicate-container-labels-are-rejected
(let [data (failure-data
(let [data (helpers/failure-data
#(builder/fixture {:seed 1}
(builder/frame :dup {:x 0 :y 0 :width 10 :height 10}
(builder/rect))
@ -205,9 +165,9 @@
(builder/group {}
(builder/frame {:x 0 :y 0 :width 50 :height 50}
(builder/rect))))
group (first (children-of instance uuid/zero))
frame (first (children-of instance (:id group)))
rect (first (children-of instance (:id frame)))]
group (first (helpers/children-of instance uuid/zero))
frame (first (helpers/children-of instance (:id group)))
rect (first (helpers/children-of instance (:id frame)))]
(t/is (= (:id group) (:parent-id frame)))
(t/is (= uuid/zero (:frame-id frame)))
(t/is (= (:id frame) (:parent-id rect)))
@ -218,44 +178,44 @@
(builder/group {}
(builder/group {}
(builder/rect {:x 5 :y 5 :width 10 :height 10}))))
outer (first (children-of instance uuid/zero))
inner (first (children-of instance (:id outer)))]
outer (first (helpers/children-of instance uuid/zero))
inner (first (helpers/children-of instance (:id outer)))]
(t/is (= [5 5 10 10]
[(:x outer) (:y outer) (:width outer) (:height outer)]))
(t/is (= [5 5 10 10]
[(:x inner) (:y inner) (:width inner) (:height inner)]))
(t/is (= 1 (count (child-ids instance (:id outer)))))))
(t/is (= 1 (count (helpers/child-ids instance (:id outer)))))))
(t/deftest empty-frame-is-allowed
(let [instance (builder/fixture {:seed 1}
(builder/frame {:x 0 :y 0 :width 10 :height 10}))
frame (first (children-of instance uuid/zero))]
(t/is (= [] (child-ids instance (:id frame))))))
frame (first (helpers/children-of instance uuid/zero))]
(t/is (= [] (helpers/child-ids instance (:id frame))))))
(t/deftest caught-empty-group-still-fails-finish
(let [state (run-scope {:seed 1}
(fn []
(try
(builder/group {})
(catch :default _ nil))))]
(let [state (helpers/run-scope {:seed 1}
(fn []
(try
(builder/group {})
(catch :default _ nil))))]
(t/is (= ::builder/unfinished-container
(:type (failure-data #(builder/finish! state)))))))
(:type (helpers/failure-data #(builder/finish! state)))))))
(t/deftest container-failure-restores-parent-and-marks-unfinished
(let [state (run-scope {:seed 1}
(fn []
(builder/rect {:x 0})
(try
(builder/frame {:x 0 :y 0 :width 10 :height 10}
(builder/rect :dup {:x 1})
(builder/rect :dup {:x 2}))
(catch :default _ nil))
(builder/rect {:x 3})))
root-children (child-ids @state uuid/zero)
(let [state (helpers/run-scope {:seed 1}
(fn []
(builder/rect {:x 0})
(try
(builder/frame {:x 0 :y 0 :width 10 :height 10}
(builder/rect :dup {:x 1})
(builder/rect :dup {:x 2}))
(catch :default _ nil))
(builder/rect {:x 3})))
root-children (helpers/child-ids @state uuid/zero)
last-child (get-in @state [:objects (last root-children)])]
(t/is (= 3 (count root-children)))
(t/is (= :rect (:type last-child)))
(t/is (= 3 (:x last-child)))
(t/is (= 1 (count (:unfinished @state))))
(t/is (= ::builder/unfinished-container
(:type (failure-data #(builder/finish! state)))))))
(:type (helpers/failure-data #(builder/finish! state)))))))

View File

@ -9,7 +9,7 @@
[app.common.schema :as sm]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[benches.render-wasm.scenes.common :as fixture]
[benches.render-wasm.scenes.common :as common]
[cljs.test :as t :include-macros true]))
(defn- sample-instance
@ -67,7 +67,7 @@
"Returns the ex-data of the validation failure, or nil when it passes."
[instance]
(try
(fixture/validate! instance)
(common/validate! instance)
nil
(catch :default cause
(ex-data cause))))
@ -75,14 +75,14 @@
(t/deftest canonical-example-is-valid
(t/testing "validate! returns the same instance"
(let [instance (sample-instance)]
(t/is (identical? instance (fixture/validate! instance)))))
(t/is (identical? instance (common/validate! instance)))))
(t/testing "the envelope schema accepts the example"
(t/is (true? (sm/validate fixture/schema:instance (sample-instance))))))
(t/is (true? (sm/validate common/schema:instance (sample-instance))))))
(t/deftest nested-hierarchy-is-valid
(let [instance (nested-instance)]
(t/is (identical? instance (fixture/validate! instance)))))
(t/is (identical? instance (common/validate! instance)))))
(t/deftest root-only-instance-is-valid
(let [root (cts/setup-shape {:id uuid/zero :type :frame :name "Root Frame"
@ -90,18 +90,17 @@
:parent-id uuid/zero :frame-id uuid/zero
:shapes []})
instance {:objects {uuid/zero root} :refs {}}]
(t/is (identical? instance (fixture/validate! instance)))
(t/is (= [uuid/zero] (mapv :id (fixture/upload-order instance))))))
(t/is (identical? instance (common/validate! instance)))
(t/is (= [uuid/zero] (mapv :id (common/upload-order instance))))))
(t/deftest upload-order-starts-at-root
(let [instance (sample-instance)
ids (mapv :id (fixture/upload-order instance))]
(t/is (= uuid/zero (first ids)))
(t/is (= [uuid/zero (fixture/ref-id instance :rect)] ids))))
ids (mapv :id (common/upload-order instance))]
(t/is (= [uuid/zero (common/ref-id instance :rect)] ids))))
(t/deftest upload-order-is-parent-before-child
(let [instance (nested-instance)
order (fixture/upload-order instance)
order (common/upload-order instance)
names (mapv :name order)
position (into {} (map-indexed (fn [index shape] [(:id shape) index]) order))]
(t/is (= ["Root Frame" "A" "A1" "A2" "B" "B1"] names))
@ -135,39 +134,39 @@
a-id shape-a
b-id shape-b}
:refs {}}]
(t/is (identical? instance (fixture/validate! instance)))
(t/is (identical? instance (common/validate! instance)))
(t/is (= ["Root Frame" "Flex" "B" "A"]
(mapv :name (fixture/upload-order instance))))))
(mapv :name (common/upload-order instance))))))
(t/deftest ref-id-resolves-labels
(let [instance (sample-instance)]
(t/is (= (uuid/custom 1 1) (fixture/ref-id instance :rect)))
(t/is (nil? (fixture/ref-id instance :missing)))))
(t/is (= (uuid/custom 1 1) (common/ref-id instance :rect)))
(t/is (nil? (common/ref-id instance :missing)))))
(t/deftest missing-root-is-rejected
(let [instance (sample-instance)
data (validation-error (update instance :objects dissoc uuid/zero))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= uuid/zero (:id data)))))
(t/deftest non-frame-root-is-rejected
(let [instance (sample-instance)
data (validation-error (assoc-in instance [:objects uuid/zero :type] :rect))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= uuid/zero (:id data)))))
(t/deftest root-with-non-zero-parent-id-is-rejected
(let [instance (sample-instance)
data (validation-error
(assoc-in instance [:objects uuid/zero :parent-id] (uuid/custom 9 9)))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= uuid/zero (:id data)))))
(t/deftest object-key-must-match-shape-id
(let [instance (sample-instance)
rect (get-in instance [:objects (fixture/ref-id instance :rect)])
rect (get-in instance [:objects (common/ref-id instance :rect)])
data (validation-error (assoc-in instance [:objects (uuid/custom 7 7)] rect))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= (:id rect) (:shape-id data)))))
(t/deftest missing-child-is-rejected
@ -175,32 +174,32 @@
bogus (uuid/custom 3 3)
data (validation-error
(assoc-in instance [:objects uuid/zero :shapes] [bogus]))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= bogus (:child data)))))
(t/deftest child-not-listed-by-parent-is-rejected
(let [instance (sample-instance)
rect-id (fixture/ref-id instance :rect)
rect-id (common/ref-id instance :rect)
data (validation-error (assoc-in instance [:objects uuid/zero :shapes] []))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "object is not reachable from the root" (:hint data)))
(t/is (= rect-id (:id data)))))
(t/deftest mismatched-parent-id-is-rejected
(let [instance (sample-instance)
rect-id (fixture/ref-id instance :rect)
rect-id (common/ref-id instance :rect)
data (validation-error
(assoc-in instance [:objects rect-id :parent-id] (uuid/custom 9 7)))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "shape :shapes lists a child with a different :parent-id" (:hint data)))
(t/is (= rect-id (:child data)))))
(t/deftest duplicate-child-ids-are-rejected
(let [instance (sample-instance)
rect-id (fixture/ref-id instance :rect)
rect-id (common/ref-id instance :rect)
data (validation-error
(assoc-in instance [:objects uuid/zero :shapes] [rect-id rect-id]))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "an object is reachable more than once" (:hint data)))
(t/is (= rect-id (:id data)))))
@ -208,7 +207,7 @@
(let [instance (sample-instance)
data (validation-error
(assoc-in instance [:objects uuid/zero :shapes] [uuid/zero]))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "root must not be a descendant of itself" (:hint data)))
(t/is (= uuid/zero (:id data)))))
@ -228,7 +227,7 @@
(assoc (get (sample-instance) :objects)
c1-id shape1
c2-id shape2)))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "object is not reachable from the root" (:hint data)))
(t/is (contains? #{c1-id c2-id} (:id data)))))
@ -248,24 +247,24 @@
(assoc-in [:objects a-id] shape-a)
(assoc-in [:objects b-id] shape-b))
data (validation-error instance)]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "shape :shapes lists a child with a different :parent-id" (:hint data)))
(t/is (= a-id (:child data)))))
(t/deftest non-vector-children-are-rejected
(let [instance (sample-instance)
rect-id (fixture/ref-id instance :rect)
rect-id (common/ref-id instance :rect)
data (validation-error (assoc-in instance [:objects rect-id :shapes] 5))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= "shape :shapes must be a vector" (:hint data)))
(t/is (= rect-id (:id data)))))
(t/deftest frame-id-mismatch-is-rejected
(let [instance (nested-instance)
a1-id (fixture/ref-id instance :a1)
a1-id (common/ref-id instance :a1)
data (validation-error
(assoc-in instance [:objects a1-id :frame-id] uuid/zero))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= :invalid-frame (:code data)))
(t/is (= a1-id (:shape-id data)))))
@ -273,45 +272,45 @@
(let [instance (sample-instance)
data (validation-error
(assoc-in instance [:refs :rect] (uuid/custom 6 6)))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= :rect (:label data)))))
(t/deftest non-uuid-ref-value-is-rejected
(let [data (validation-error (assoc-in (sample-instance) [:refs :rect] "not-a-uuid"))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (some? (::sm/explain data)))))
(t/deftest missing-refs-key-is-rejected
(let [data (validation-error (dissoc (sample-instance) :refs))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (some? (::sm/explain data)))))
(t/deftest non-canonical-shape-is-rejected
(let [instance (sample-instance)
rect-id (fixture/ref-id instance :rect)
rect-id (common/ref-id instance :rect)
data (validation-error (assoc-in instance [:objects rect-id] {:id rect-id}))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (some? (::sm/explain data)))))
(t/deftest malformed-envelopes-are-rejected
(t/testing "empty objects map"
(let [data (validation-error {:objects {} :refs {}})]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (= uuid/zero (:id data)))))
(t/testing "nil instance"
(let [data (validation-error nil)]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (some? (::sm/explain data)))))
(t/testing "nil shape value"
(let [instance (sample-instance)
rect-id (fixture/ref-id instance :rect)
rect-id (common/ref-id instance :rect)
data (validation-error (assoc-in instance [:objects rect-id] nil))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (some? (::sm/explain data)))))
(t/testing "nil ref value"
(let [data (validation-error (assoc-in (sample-instance) [:refs :rect] nil))]
(t/is (= ::fixture/invalid-fixture (:type data)))
(t/is (= ::common/invalid-fixture (:type data)))
(t/is (some? (::sm/explain data))))))

View File

@ -0,0 +1,49 @@
(ns frontend-tests.benches.test-helpers
"Shared helpers for renderer benchmark fixture tests.
These helpers assert nothing about shapes or the renderer. They only
capture errors, read root children in order, and run raw scopes so each
test namespace tells a complete story without copying bodies."
(:require
[app.common.uuid :as uuid]
[benches.render-wasm.scenes.builder :as builder]))
(defn failure-data
"Returns the ex-data of the thrown failure, or nil when `f` passes."
[f]
(try
(f)
nil
(catch :default cause
(ex-data cause))))
(defn child-ids
"Ids listed in the :shapes vector of `id`."
[instance id]
(get-in instance [:objects id :shapes]))
(defn children-of
"Child shapes of `id` in order."
[instance id]
(mapv (:objects instance) (child-ids instance id)))
(defn root-shapes
"Root children of a fixture instance in creation order."
[instance]
(children-of instance uuid/zero))
(defn run-scope
"Runs `f` inside a raw scope and returns the scope state, so tests can
inspect partial builds without calling finish!."
[params f]
(let [state (builder/start params)]
(binding [builder/*state* state
builder/*defaults* {}]
(f))
state))
(defn selrect
"Selrect of `shape` as an [x y width height] tuple."
[shape]
[(:x (:selrect shape)) (:y (:selrect shape))
(:width (:selrect shape)) (:height (:selrect shape))])