🚧 WIP 24 Relocate all code, delete old benchmarks

This commit is contained in:
Miguel de Benito Delgado 2026-10-06 12:32:05 +02:00
parent 88059fff86
commit 98393a13c4
42 changed files with 266 additions and 46 deletions

View File

@ -39,7 +39,7 @@
"^(?:backend|frontend|exporter|common)/deps.edn"
"^(?:backend|frontend|exporter|common)/scripts/"
"^(?:backend|frontend|exporter|common)/dev/"
"^(?:backend|frontend|exporter|common)/test/(?!benches/|frontend_tests/benches/)"]
"^(?:backend|frontend|exporter|common)/test/(?!performance_tests/)"]
:linter-name true}

View File

@ -1,4 +1,4 @@
{:paths ["src" "vendor" "resources" "test"]
{:paths ["src" "vendor" "resources" "test" "performance"]
:deps
{;; Carries `app.common.render-wasm.*`, shared with the headless exporter.
penpot/common
@ -37,8 +37,7 @@
instaparse/instaparse {:mvn/version "1.5.0"}
garden/garden {:git/url "https://github.com/noprompt/garden"
:git/sha "05590ecb5f6fa670856f3d1ab400aa4961047480"}
}
:git/sha "05590ecb5f6fa670856f3d1ab400aa4961047480"}}
:aliases
{:outdated
@ -70,5 +69,4 @@
:renderer-bench
{:main-opts ["-m" "shadow.cljs.devtools.cli"]
:jvm-opts ["-Dpenpot.wasm.profile-marks=false"]}
}}
:jvm-opts ["-Dpenpot.wasm.profile-marks=false"]}}}

View File

@ -26,19 +26,21 @@
"build:app:main": "clojure -M:dev:shadow-cljs release main worker",
"build:app:worker": "clojure -M:dev:shadow-cljs release worker",
"build:app": "pnpm run clear:shadow-cache && pnpm run build:app:main && pnpm run build:app:libs",
"check-fmt:clj": "cljfmt check --parallel=true src/ test/",
"check-fmt:clj": "cljfmt check --parallel=true src/ performance/ test/",
"check-fmt:js": "prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js",
"check-fmt:scss": "prettier -c resources/styles -c src/**/*.scss",
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
"fmt:clj": "cljfmt fix --parallel=true src/ performance/ test/",
"fmt:js": "prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js -w",
"fmt:scss": "prettier -c resources/styles -c src/**/*.scss -w",
"lint:clj": "clj-kondo --fail-level error --config-dir ../.clj-kondo --lint ../common/src src/ test/benches/ test/frontend_tests/benches/",
"lint:clj": "clj-kondo --fail-level error --config-dir ../.clj-kondo --lint ../common/src src/ performance/ test/performance_tests/render_wasm/",
"lint:js": "exit 0",
"lint:renderer-benchmarks": "clj-kondo --config-dir ../.clj-kondo --lint test/benches/ test/frontend_tests/benches/",
"lint:performance": "clj-kondo --config-dir ../.clj-kondo --lint performance/ test/performance_tests/render_wasm/",
"lint:scss": "pnpm exec stylelint '{src,resources}/**/*.scss'",
"build:test": "pnpm run build:wasm && clojure -M:dev:shadow-cljs compile test",
"build:performance-tests": "clojure -M:dev:shadow-cljs compile performance-tests",
"test": "pnpm run build:test && node target/tests/test.js",
"test:quiet": "node ./scripts/test-quiet.js",
"test:performance-tests": "pnpm run build:performance-tests && node target/performance-tests/test.js",
"test:storybook": "vitest run --project=storybook",
"watch:test": "mkdir -p target/tests && concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests --exec 'node target/tests/test.js'\"",
"test:e2e": "playwright test --project default",

View File

@ -32,7 +32,7 @@
Follow README.md's prepared renderer release commands first.
clojure -M:dev:renderer-bench release bench-render-wasm-browser
pnpm run build:renderer-benchmarks:pilot
node target/renderer-benchmarks/pilot.cjs --seed 42 --screenshot /tmp/pilot-rects.png
node target/performance/render-wasm/pilot.cjs --seed 42 --screenshot /tmp/pilot-rects.png
The pilot serves the release browser bundle, so compile
`bench-render-wasm-browser` first (after the prepared renderer build
@ -62,10 +62,10 @@
(defn- frontend-root
"Resolves the frontend checkout root from the compiled pilot location
(`target/renderer-benchmarks/pilot.cjs`, next to `runner.cjs`). Ticket 10
(`target/performance/render-wasm/pilot.cjs`, next to `runner.cjs`). Ticket 10
owns command cwd resolution; the pilot pins its own roots instead."
[]
(path/resolve js/__dirname ".." ".."))
(path/resolve js/__dirname ".." ".." ".."))
(def ^:private mime
{".html" "text/html"
@ -209,9 +209,9 @@
(trace "pilot: parsing args")
(let [opts (parse-args (or argv []))
root (frontend-root)
bundle (path/join root "target" "renderer-benchmarks" "browser")
bundle (path/join root "target" "performance" "render-wasm" "browser")
js-dir (path/join root "resources" "public" "js")
html (path/join root "test" "benches" "render_wasm" "browser" "pilot.html")
html (path/join root "performance" "bench" "render_wasm" "browser" "pilot.html")
seed (:seed opts)
selected (some #(when (= (:id %) (:case-id opts)) %)
(cases/collect-cases {:master-seed seed}))

View File

@ -30,7 +30,7 @@
(defn- usage
[summary]
(str "Usage: node target/renderer-benchmarks/runner.cjs [options]\n\n"
(str "Usage: node target/performance/render-wasm/runner.cjs [options]\n\n"
"Options:\n"
summary "\n\n"
"No command is implemented yet. Upcoming tickets add:\n"
@ -57,9 +57,9 @@
(seq errors)
(fail! (str/join "\n" errors))
;; TODO(mem:render-wasm/performance/cljs-rewrite/10-process-build-and-server,
;; mem:render-wasm/performance/cljs-rewrite/11-runner-and-failure-accounting,
;; mem:render-wasm/performance/cljs-rewrite/14-custom-measured-operations):
;; TODO(mem:render-wasm/performance_tests/cljs-rewrite/10-process-build-and-server,
;; mem:render-wasm/performance_tests/cljs-rewrite/11-runner-and-failure-accounting,
;; mem:render-wasm/performance_tests/cljs-rewrite/14-custom-measured-operations):
;; dispatch `run`, `ab` and `compare` instead of failing.
(seq arguments)
(fail! (str "Unknown command: " (str/join " " arguments) "\nTry --help"))

View File

@ -177,10 +177,27 @@
:elide-asserts true
:anon-fn-naming-policy :off}}}
:performance-tests
{:target :esm
:output-dir "target/performance-tests"
:runtime :custom
:js-options {:js-provider :import}
:modules
{:test {:init-fn performance-tests.render-wasm.runner/init
:prepend-js ";if (typeof globalThis.navigator?.userAgent === 'undefined') { globalThis.navigator = {userAgent: ''}; };"}}
:release
{:compiler-options
{:fn-invoke-direct true
:elide-asserts true
:anon-fn-naming-policy :off}}}
:bench-render-wasm-runner
{:target :node-script
:output-to "target/renderer-benchmarks/runner.cjs"
:output-dir "target/renderer-benchmarks/runner/"
:output-to "target/performance/render-wasm/runner.cjs"
:output-dir "target/performance/render-wasm/runner/"
:main benches.render-wasm.node.runner/-main
:devtools {:autoload false}
@ -197,8 +214,8 @@
:bench-render-wasm-pilot
{:target :node-script
:output-to "target/renderer-benchmarks/pilot.cjs"
:output-dir "target/renderer-benchmarks/pilot/"
:output-to "target/performance/render-wasm/pilot.cjs"
:output-dir "target/performance/render-wasm/pilot/"
:main benches.render-wasm.node.pilot/-main
:devtools {:autoload false}
@ -215,7 +232,7 @@
:bench-render-wasm-browser
{:target :esm
:output-dir "target/renderer-benchmarks/browser/"
:output-dir "target/performance/render-wasm/browser/"
;; Bundle npm deps (date-fns via app.common): the pilot serves plain
;; static files with no import-map resolver. TODO(10): revisit when
;; ticket 10 owns staging; artifacts must stay self-contained.

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.boolean-test
(ns performance-tests.render-wasm.boolean-test
(:require
[app.common.geom.point :as gpt]
[app.common.geom.shapes.intersect :as gint]
@ -14,7 +14,7 @@
[benches.render-wasm.builder :as b :include-macros true]
[benches.render-wasm.snapshot :as snapshot]
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
[performance-tests.render-wasm.test-helpers :as helpers]))
(t/deftest difference-of-overlapping-rects-keeps-hierarchy-and-bounds
(let [red {:fill-color "#ff0000" :fill-opacity 1}

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.builder-test
(ns performance-tests.render-wasm.builder-test
"Tests canonical scene construction and the shared seeded number generator.
Fixed random sequences preserve scene draws during generator extraction."
(:require
@ -15,7 +15,7 @@
[benches.render-wasm.scenes.rects]
[benches.render-wasm.snapshot :as snapshot]
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
[performance-tests.render-wasm.test-helpers :as helpers]))
(defn- build
([] (build {}))

View File

@ -4,12 +4,12 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.containers-test
(ns performance-tests.render-wasm.containers-test
(:require
[app.common.uuid :as uuid]
[benches.render-wasm.builder :as builder :include-macros true]
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
[performance-tests.render-wasm.test-helpers :as helpers]))
(t/deftest frame-attaches-children-in-order
(let [instance (builder/scene {:seed 1}

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.contracts-test
(ns performance-tests.render-wasm.contracts-test
(:require
[app.common.schema :as sm]
[app.common.transit :as transit]
@ -425,7 +425,7 @@
(t/is (decl/transit-round-trips? (first collected))))
(t/is (empty? @body-calls))
(let [entry (decl/registered-case :contracts-scene/case)]
(t/is (= "frontend-tests.benches.contracts-test" (:ns entry)))
(t/is (= "performance-tests.render-wasm.contracts-test" (:ns entry)))
(t/is (fn? (:run! entry)))
(t/is (= :runtime ((:run! entry) :runtime)))
(t/is (= [:touched] @body-calls))))

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.fingerprint-test
(ns performance-tests.render-wasm.fingerprint-test
"Tests content identity with production records, paths and collection types.
Validated scene snapshots retain child order even when maps reorder."
(:require

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.geometry-scenes-test
(ns performance-tests.render-wasm.geometry-scenes-test
(:require
[app.common.types.path :as path]
[app.common.uuid :as uuid]
@ -16,7 +16,7 @@
[benches.render-wasm.scenes.plus]
[benches.render-wasm.snapshot :as snapshot]
[cljs.test :as t :include-macros true]
[frontend-tests.benches.test-helpers :as helpers]))
[performance-tests.render-wasm.test-helpers :as helpers]))
(t/deftest curved-paths-use-canonical-content-and-stable-order
(let [params {:count 12 :width 1920 :height 1080 :seed 42}

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.lifecycle-test
(ns performance-tests.render-wasm.lifecycle-test
"Tests the browser bridge with fake DOM and renderer boundaries.
It covers bridge input, ownership, disposal and `load-scene` with fake
@ -253,6 +253,8 @@
prev-window (unchecked-get js/globalThis "window")
prev-import (unchecked-get js/globalThis "dynamicImport")
prev-performance (unchecked-get js/globalThis "performance")
prev-now-fn (unchecked-get prev-performance "now")
prev-now-desc (js/Object.getOwnPropertyDescriptor prev-performance "now")
prev-now (unchecked-get js/globalThis "__benchNow")
prev-script (unchecked-get js/globalThis "__benchRenderScript")
prev-calls (unchecked-get js/globalThis "__benchCalls")
@ -283,19 +285,25 @@
(cb timestamp))
(cb 0)))
nil)}
performance #js {:now (fn []
(if-some [clock (unchecked-get js/globalThis "__benchNow")]
clock
(.now prev-performance)))}
fake-now (fn []
(if-some [clock (unchecked-get js/globalThis "__benchNow")]
clock
(.call prev-now-fn prev-performance)))
dynamic-import (fn [url] (js/eval (str "import(" (pr-str url) ")")))]
(unchecked-set js/globalThis "document" document)
(unchecked-set js/globalThis "window" window)
(unchecked-set js/globalThis "performance" performance)
;; Patch `now` on the real object: shadow hangs the compiled
;; `performance.*` namespaces off the global `performance` object, so
;; replacing that global hides every `benches.render-wasm.*` var.
(js/Object.defineProperty prev-performance "now"
#js {:value fake-now :configurable true :writable true})
(unchecked-set js/globalThis "dynamicImport" dynamic-import)
(fn restore! []
(unchecked-set js/globalThis "document" prev-document)
(unchecked-set js/globalThis "window" prev-window)
(unchecked-set js/globalThis "performance" prev-performance)
(if (undefined? prev-now-desc)
(js-delete prev-performance "now")
(js/Object.defineProperty prev-performance "now" prev-now-desc))
(unchecked-set js/globalThis "__benchNow" prev-now)
(unchecked-set js/globalThis "__benchRenderScript" prev-script)
(unchecked-set js/globalThis "__benchCalls" prev-calls)

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.protocol-test
(ns performance-tests.render-wasm.protocol-test
"Tests drain, restore and interact with an injected clock and renderer.
Browser entry tests cover load and warm pan with a fake module."
(:require

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.report-test
(ns performance-tests.render-wasm.report-test
"Pure tests for raw records, exploratory statistics and saved-run comparisons.
Fixtures use saved case definitions without importing a registry or renderer."
(:require

View File

@ -0,0 +1,195 @@
;; 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 performance-tests.render-wasm.runner
(:require
[app.common.logging :as l]
[cljs.test :as t]
[clojure.string :as str]
[clojure.tools.cli :refer [parse-opts]]
[goog.object :as gobj]
[performance-tests.render-wasm.boolean-test]
[performance-tests.render-wasm.builder-test]
[performance-tests.render-wasm.containers-test]
[performance-tests.render-wasm.contracts-test]
[performance-tests.render-wasm.fingerprint-test]
[performance-tests.render-wasm.geometry-scenes-test]
[performance-tests.render-wasm.lifecycle-test]
[performance-tests.render-wasm.protocol-test]
[performance-tests.render-wasm.report-test]
[performance-tests.render-wasm.scene-snapshot-test]))
(enable-console-print!)
(defmethod t/report [:cljs.test/default :begin-test-var] [m]
(let [v (:var m)]
(println (str " ▸ " (:ns (meta v)) "/" (:name (meta v))))))
(defmethod t/report [:cljs.test/default :end-run-tests] [m]
(if (cljs.test/successful? m)
(.exit js/process 0)
(.exit js/process 1)))
(def test-namespaces
['performance-tests.render-wasm.boolean-test
'performance-tests.render-wasm.builder-test
'performance-tests.render-wasm.containers-test
'performance-tests.render-wasm.contracts-test
'performance-tests.render-wasm.fingerprint-test
'performance-tests.render-wasm.geometry-scenes-test
'performance-tests.render-wasm.lifecycle-test
'performance-tests.render-wasm.protocol-test
'performance-tests.render-wasm.report-test
'performance-tests.render-wasm.scene-snapshot-test])
(assert (every? find-ns-obj test-namespaces)
"test-namespaces contains a namespace that isn't required in runner.cljs")
;; This runner mirrors frontend-tests.runner for the bench-only
;; :performance-tests target. Both runners need forwarded CLI args, focused
;; namespace/var execution, fixture preservation, and app log-level
;; setup. The logic stays local to avoid cross-target test classpath
;; coupling.
(def ^:private log-levels
#{:trace :debug :info :warn :error})
(def cli-options
[["-f" "--focus FOCUS" "Run one test namespace or one test var, e.g. performance-tests.render-wasm.contracts-test"]
["-l" "--log-level LEVEL" "Set app logger level: trace|debug|info|warn|error"
:parse-fn keyword
:validate [log-levels "must be one of trace, debug, info, warn, error"]]
["-h" "--help"]])
(defn- argv
[]
(let [args (->> (.-argv js/process)
(array-seq)
(drop 2))]
;; `pnpm run test:performance-tests -- --focus ...` forwards the separator to
;; the node process, so drop one leading `--` before handing args to
;; tools.cli.
(cond-> args
(= "--" (first args)) rest)))
(defn- usage
[summary]
(str "Usage: pnpm run test:performance-tests -- [options]\n\n"
"Options:\n"
summary "\n\n"
"Focus examples:\n"
" pnpm run test:performance-tests -- --focus performance-tests.render-wasm.contracts-test\n"
" pnpm run test:performance-tests -- --focus performance-tests.render-wasm.contracts-test/some-test-var\n\n"
"Log level example (quiets app logging during the run):\n"
" pnpm run test:performance-tests -- --focus performance-tests.render-wasm.contracts-test --log-level warn"))
(defn- fail!
[message]
(js/console.error message)
(.exit js/process 1))
(defn- parse-focus
[focus]
(let [[ns-name test-name & extra] (str/split focus #"/")]
(cond
(or (str/blank? ns-name) (seq extra))
(fail! (str "Invalid --focus value: " focus))
(some? test-name)
{:ns (symbol ns-name) :test test-name}
:else
{:ns (symbol ns-name)})))
(defn- fixture-value
[ns-obj fixture-name]
(let [value (gobj/get ns-obj (munge fixture-name))]
(when-not (undefined? value)
value)))
(defn- ns-test-vars
[ns-sym]
(when-let [ns-obj (find-ns-obj ns-sym)]
(->> (js-keys ns-obj)
(keep (fn [key]
(some-> (gobj/get ns-obj key)
(.-cljs$lang$var))))
(filter (comp :test meta))
(sort-by (comp :line meta)))))
(defn- ns-fixtures
[ns-sym vars]
(when-let [ns-obj (find-ns-obj ns-sym)]
(let [ns-key (or (some-> vars first meta :ns) ns-sym)
once-fixtures (fixture-value ns-obj "cljs-test-once-fixtures")
each-fixtures (fixture-value ns-obj "cljs-test-each-fixtures")]
{:once (when once-fixtures {ns-key once-fixtures})
:each (when each-fixtures {ns-key each-fixtures})})))
(defn- selected-tests
[{:keys [ns test]}]
(when-not (some #{ns} test-namespaces)
(fail! (str "Unknown test namespace: " ns)))
(let [vars (vec (ns-test-vars ns))]
(when (empty? vars)
(fail! (str "No tests found in namespace: " ns)))
(if test
(let [test-sym (symbol test)
test-var (some #(when (= test-sym (:name (meta %))) %) vars)]
(if test-var
{:vars [test-var]
:fixtures (ns-fixtures ns [test-var])}
(fail! (str "Unknown test var: " ns "/" test))))
{:vars vars
:fixtures (ns-fixtures ns vars)})))
(defn- merge-fixtures
[fixtures]
{:once (apply merge (keep :once fixtures))
:each (apply merge (keep :each fixtures))})
(defn- run-test-vars!
[tests]
(let [vars (vec (mapcat :vars tests))
fixtures (merge-fixtures (map :fixtures tests))
env (assoc (t/empty-env)
:once-fixtures (:once fixtures)
:each-fixtures (:each fixtures))
summary (volatile! {:test 0 :pass 0 :fail 0 :error 0 :type :summary})]
(t/set-env! env)
(t/run-block
(concat (t/test-vars-block vars)
[(fn []
(vswap! summary
(partial merge-with +)
(:report-counters (t/get-current-env))))
(fn []
(t/report @summary)
(t/report (assoc @summary :type :end-run-tests)))]))))
(defn- run-focused-test!
[focus]
(run-test-vars! [(selected-tests (parse-focus focus))]))
(defn init
[]
(let [{:keys [options errors summary]} (parse-opts (argv) cli-options)]
(cond
(seq errors)
(fail! (str/join "\n" errors))
(:help options)
(do
(println (usage summary))
(.exit js/process 0))
:else
(do
(l/setup! {:app (or (:log-level options) :warn)})
(if (:focus options)
(run-focused-test! (:focus options))
(run-test-vars! (map #(selected-tests {:ns %}) test-namespaces)))))))

View File

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.benches.scene-snapshot-test
(ns performance-tests.render-wasm.scene-snapshot-test
(:require
[app.common.schema :as sm]
[app.common.types.shape :as cts]

View File

@ -1,4 +1,4 @@
(ns frontend-tests.benches.test-helpers
(ns performance-tests.render-wasm.test-helpers
"Shared helpers for renderer benchmark scene tests.
These helpers assert nothing about shapes or the renderer. They only