🚧 WIP Step 20 test last round of features

This commit is contained in:
Miguel de Benito Delgado 2026-10-02 13:11:57 +02:00
parent 27e4597b45
commit 23746893e6
4 changed files with 189 additions and 9 deletions

View File

@ -83,6 +83,13 @@
(let [collected (rect-cases)]
(t/is (= [:rects/load :rects/pan :rects/zoom] (mapv :id collected)))))
(t/deftest real-rectangle-bodies-stay-out-of-node-descriptors
(let [ids [:rects/load :rects/pan :rects/zoom]]
(t/is (every? #(fn? (:run! (core/registered-case %))) ids))
(t/is (= ids (mapv :id (rect-cases)))
"collection succeeds with the executable bodies present")
(t/is (every? #(not (contains? % :run!)) (rect-cases)))))
(t/deftest collected-cases-survive-the-wire
(doseq [case-desc (rect-cases)]
(t/is (not (contains? case-desc :run!)) (str (:id case-desc)))

View File

@ -0,0 +1,81 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.geometry-scenes-test
(:require
[app.common.types.path :as path]
[app.common.uuid :as uuid]
[benches.render-wasm.cases :as cases]
[benches.render-wasm.scenes.common :as common]
[benches.render-wasm.scenes.masks :as masks]
[benches.render-wasm.scenes.paths :as paths]
[benches.render-wasm.scenes.plus :as plus]
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
(t/deftest curved-paths-use-canonical-content-and-stable-order
(let [params {:count 12 :width 1920 :height 1080 :seed 42}
scene (paths/build params)
again (paths/build params)
shapes (helpers/children-of scene uuid/zero)
first (first shapes)
content (vec (:content first))]
(t/is (= 12 (count shapes)))
(t/is (= scene again) "same scene parameters and seed reproduce content and ids")
(t/is (path/content? (:content first)))
(t/is (= [:move-to :curve-to :line-to :close-path]
(mapv :command content)))
(t/is (pos? (:width (:selrect first))))
(t/is (pos? (:height (:selrect first))))
(t/is (= (mapv :id shapes) (mapv :id (rest (common/upload-order scene)))))
(t/is (= scene (common/validate! scene)))))
(t/deftest plus-paths-have-two-crossing-open-lines
(let [scene (plus/build {:count 8 :width 1920 :height 1080 :seed 42})
first (first (helpers/children-of scene uuid/zero))
segments (vec (:content first))
[a b c d] segments]
(t/is (= 8 (count (helpers/children-of scene uuid/zero))))
(t/is (= [:move-to :line-to :move-to :line-to]
(mapv :command segments)))
(t/is (= (get-in a [:params :y]) (get-in b [:params :y]))
"the first line is horizontal")
(t/is (< (get-in a [:params :x]) (get-in b [:params :x])))
(t/is (= (get-in c [:params :x]) (get-in d [:params :x]))
"the second line is vertical")
(t/is (< (get-in c [:params :y]) (get-in d [:params :y])))
(t/is (seq (:strokes first)))
(t/is (= scene (common/validate! scene)))))
(t/deftest mask-child-order-and-descendants-are-reachable
(let [scene (masks/build {:width 1920 :height 1080 :seed 42})
groups (helpers/children-of scene uuid/zero)
group (first groups)
children (helpers/children-of scene (:id group))
mask (first children)
uploaded (common/upload-order scene)]
(t/is (= 24 (count groups)))
(t/is (= 241 (count (:objects scene))) "root, groups and their nine children")
(t/is (= (:id group) (get-in scene [:refs [:mask 0]])))
(t/is (true? (:masked-group group)))
(t/is (= :circle (:type mask)) "the first child is the mask")
(t/is (= 9 (count children)))
(t/is (every? #(= (:id group) (:parent-id %)) children))
(t/is (= (:selrect mask) (:selrect group)))
(t/is (= uuid/zero (:id (first uploaded))))
(t/is (= (:id group) (:id (second uploaded))))
(t/is (= (:id mask) (:id (nth uploaded 2))))
(t/is (= (count (:objects scene)) (count uploaded)))
(t/is (= scene (common/validate! scene)))))
(t/deftest camera-cases-share-each-scene-seed
(doseq [scene-id [:paths :plus :masks]]
(let [selected (->> (cases/collect-cases {:master-seed 42})
(filter #(= scene-id (:scene %)))
vec)]
(t/is (= #{"load" "pan" "zoom"} (set (map (comp name :id) selected))))
(t/is (= 1 (count (set (map :scene-seed selected)))))
(t/is (every? #(not (contains? % :run!)) selected)))))

View File

@ -35,11 +35,13 @@
"Transit request for `browser/load-scene`. `overrides` change the
collected case descriptor."
[seed overrides]
(transit/encode-str
{:seed seed
:case (merge (load-case) overrides)
:module-url (factory-url)
:wasm-url "./fake.wasm"}))
(let [base (or (some #(when (= (:id %) (:id overrides)) %)
(cases/collect-cases {:master-seed 42}))
(load-case))]
(transit/encode-str
{:case (merge base {:scene-seed seed} overrides)
:module-url (factory-url)
:wasm-url "./fake.wasm"})))
(defn- expect-failed-args
"Calls `load-scene` with `args` (never touching DOM/FFI: validation runs
@ -62,11 +64,11 @@
(t/deftest missing-seed-fails-before-timers
(t/async done
(expect-failed-args (transit/encode-str {:case (load-case)}) true done)))
(expect-failed-args (transit/encode-str {:case (dissoc (load-case) :scene-seed)}) true done)))
(t/deftest missing-case-fails-before-timers
(t/async done
(expect-failed-args (transit/encode-str {:seed 42}) true done)))
(expect-failed-args (transit/encode-str {}) true done)))
(t/deftest malformed-transit-fails-before-timers
(t/async done
@ -87,6 +89,22 @@
true
done)))
(t/deftest missing-compiled-body-fails-before-claim
(t/async done
(expect-failed-args (scene-args 42 {:id :rects/missing}) true done)))
(t/deftest wrong-scene-body-fails-before-claim
(let [id :rects/wrong-scene
descriptor (load-case)]
(core/register-case! {:id id :scene :other :ns "lifecycle-test"
:run! (fn [_] nil)})
(try
(let [request (transit/encode-str {:case (assoc descriptor :id id)})
parsed (@#'browser/parse-load-request request)]
(t/is (= "invalid-args" (get-in parsed [:error :phase]))))
(finally
(core/unregister-case! id)))))
(t/deftest non-finite-view-values-fail-before-timers
(t/async done
(expect-failed-args (scene-args 42 {:view {:scale js/Infinity :x 0 :y 0}})
@ -418,6 +436,20 @@
(cleanup)
(done)))))))
(t/deftest rejected-camera-body-releases-owner
(t/async done
(with-entry-env
(fn [cleanup]
(unchecked-set js/globalThis "__benchThrowIn" "_render_from_cache")
(load-result (scene-args 7 {:id :rects/pan :context :reuse})
(fn [m]
(t/is (= "failed" (:status m)))
(t/is (= "interact" (:phase m)))
(t/is (= 1 (effect-count (read-calls) "remove")))
(t/is (nil? @@#'browser/canvas*))
(cleanup)
(done)))))))
(t/deftest rejected-import-resolves-module-init-failure
(t/async done
;; Needs the entry env for the import polyfill, even though the import
@ -425,8 +457,7 @@
(with-entry-env
(fn [cleanup]
(load-result (transit/encode-str
{:seed 7
:case (load-case)
{:case (assoc (load-case) :scene-seed 7)
:module-url "data:text/javascript,this is not valid javascript((("})
(fn [m]
(t/is (= "failed" (:status m)))

View File

@ -220,3 +220,64 @@
(.catch (fn [cause]
(t/is false (str "must resolve, threw: " cause))
(done)))))))
(t/deftest camera-session-chains-promises-and-records-authored-sleep
(t/async done
(let [clock (fake-clock 0)
views (atom [])
events (atom [])
hooks (assoc (drain-hooks {:clock clock :script [2] :calls (atom [])})
:frame (fn []
(swap! events conj :frame)
((:advance! clock) 16)
(js/Promise.resolve ((:now clock))))
:set-view (fn [view]
(swap! views conj view)
((:advance! clock) 2))
:render-from-cache (fn []
(swap! events conj :cached)
((:advance! clock) 3))
:set-view-end (fn []
(swap! events conj :end)
((:advance! clock) 5)))
base {:scale 1 :x 0 :y 0}
target {:scale 2 :x 100 :y 40}]
(-> (protocol/start-camera! {:hooks hooks :view base} {:settle-ms 100})
(protocol/animate-view! {:to target :steps 3})
(protocol/sleep! 25)
(protocol/finish-camera!)
(.then (fn [m]
(t/is (apply < (map :x @views))
"intermediate camera views advance toward the target")
(t/is (= target (last @views)))
(t/is (= [:frame :cached :frame :cached :frame :cached :end] @events))
(t/is (= 3 (count (:cached-slices m))))
(t/is (= [5 5 5] (mapv :view-and-preview-ms (:cached-slices m)))
"call span also includes the view update")
(t/is (= [3 3 3] (mapv :duration-ms (:cached-slices m))))
(t/is (= [{:requested-ms 25 :actual-ms 25}] (:authored-sleeps m)))
(t/is (= 88 (:active-ms m))
"three rAF waits, view/cache calls and authored pause")
(t/is (= 100 (:settling-actual-ms m)))
(t/is (= 193 (:interact-ms m))
"active, settle and camera end are one span")
(done)))
(.catch (fn [cause]
(t/is false (str "must resolve, threw: " cause))
(done)))))))
(t/deftest camera-session-rejects-hook-errors
(t/async done
(let [clock (fake-clock 0)
hooks (assoc (drain-hooks {:clock clock :script [2] :calls (atom [])})
:render-from-cache (fn [] (throw (ex-info "cache failed" {:phase "preview"}))))]
(-> (protocol/start-camera! {:hooks hooks :view {:scale 1 :x 0 :y 0}}
{:settle-ms 100})
(protocol/preview-view! {:scale 1 :x 10 :y 0})
(protocol/finish-camera!)
(.then (fn [_]
(t/is false "preview error must reject")
(done)))
(.catch (fn [cause]
(t/is (= "cache failed" (ex-message cause)))
(done)))))))