ebox/tests/ebox-style-schema-tests.el
Kinneyzhang 79f5bc23d1 feat: add CSS sizing and native text interaction capabilities
Normalize size units and intrinsic sizing across Elisp and native layout. Add help, pointer, hover-style and keymap support with reusable interaction adapters.

Keep content updates local, preserve scroll caches and hover borders, and avoid rebuilding retained plans and ownership metadata for stable geometry.

Validation: make check and native-rust-tests passed; targeted native interaction and scroll publication regressions passed.
2026-09-09 22:25:18 +08:00

292 lines
13 KiB
EmacsLisp

;;; ebox-style-schema-tests.el --- M2a style schema truth gates -*- lexical-binding: t; -*-
;;; Code:
(require 'ert)
(require 'cl-lib)
(require 'benchmark)
(require 'ebox)
(require 'ebox-fixtures)
(defconst ebox-style-schema-test--legacy-nondefault-rules
'((:padding :expand (:padding-top :padding-right
:padding-bottom :padding-left)
:mode trbl)
(:padding-inline :expand (:padding-left :padding-right) :mode pair)
(:padding-block :expand (:padding-top :padding-bottom) :mode pair)
(:padding-top :to :padding-top-height :conv line)
(:padding-block-start :to :padding-top-height :conv line)
(:padding-right :to :padding-right-pixel :conv pixel)
(:padding-inline-end :to :padding-right-pixel :conv pixel)
(:padding-bottom :to :padding-bottom-height :conv line)
(:padding-block-end :to :padding-bottom-height :conv line)
(:padding-left :to :padding-left-pixel :conv pixel)
(:padding-inline-start :to :padding-left-pixel :conv pixel)
(:margin :expand (:margin-top :margin-right
:margin-bottom :margin-left)
:mode trbl)
(:margin-inline :expand (:margin-left :margin-right) :mode pair)
(:margin-block :expand (:margin-top :margin-bottom) :mode pair)
(:margin-top :to :margin-top-height :conv line)
(:margin-block-start :to :margin-top-height :conv line)
(:margin-right :to :margin-right-pixel :conv pixel)
(:margin-inline-end :to :margin-right-pixel :conv pixel)
(:margin-bottom :to :margin-bottom-height :conv line)
(:margin-block-end :to :margin-bottom-height :conv line)
(:margin-left :to :margin-left-pixel :conv pixel)
(:margin-inline-start :to :margin-left-pixel :conv pixel)
(:border :expand (:border-top :border-right
:border-bottom :border-left)
:mode same)
(:border-top :expand (:border-top-width
:border-top-style :border-top-color)
:mode wsc)
(:border-right :expand (:border-right-width
:border-right-style :border-right-color)
:mode wsc)
(:border-bottom :expand (:border-bottom-width
:border-bottom-style :border-bottom-color)
:mode wsc)
(:border-left :expand (:border-left-width
:border-left-style :border-left-color)
:mode wsc)
(:border-width :expand (:border-top-width :border-right-width
:border-bottom-width
:border-left-width)
:mode trbl)
(:border-style :expand (:border-top-style :border-right-style
:border-bottom-style
:border-left-style)
:mode trbl)
(:border-color :expand (:border-top-color :border-right-color
:border-bottom-color
:border-left-color)
:mode trbl)
(:border-top-width :to :border-top-pixel :conv border-pixel)
(:border-top-color :to :border-top-color :conv color)
(:border-bottom-width :to :border-bottom-pixel :conv border-pixel)
(:border-bottom-color :to :border-bottom-color :conv color)
(:border-left-width :to :border-left-pixel :conv border-pixel)
(:border-left-color :to :border-left-color :conv color)
(:border-right-width :to :border-right-pixel :conv border-pixel)
(:border-right-color :to :border-right-color :conv color)
(:width :to :width :conv preferred-size)
(:min-width :to :min-width :conv min-size)
(:max-width :to :max-width :conv max-size)
(:outer :to nil)
(:color :to :color :conv color)
(:background-color :to :bgcolor :conv color)
(:bgcolor :to :bgcolor :conv color)
(:text-decoration-color :to :text-decoration-color :conv color)
(:display :to nil))
"Non-default projection rules from the pre-E4 engine table.")
(defun ebox-style-schema-test--legacy-rule (property)
"Return pre-E4 projection rule for PROPERTY."
(cdr (assq property ebox-style-schema-test--legacy-nondefault-rules)))
(defun ebox-style-schema-test--current-explicit-rules ()
"Return every definition-owned and internal non-default projection rule."
(append
(cl-mapcan
(lambda (definition)
(when-let* ((rule (plist-get definition :engine-projection)))
(mapcar (lambda (name) (cons name (copy-tree rule)))
(cons (plist-get definition :name)
(copy-sequence (plist-get definition :aliases))))))
ebox-style--property-definitions)
(copy-tree ebox-style--internal-engine-projection-rules)))
(defun ebox-style-schema-test--sorted-rules (rules)
"Return a stable detached ordering of projection RULES."
(sort (copy-tree rules)
(lambda (left right)
(string< (symbol-name (car left))
(symbol-name (car right))))))
(defun ebox-style-schema-test--legacy-rule-comparisons (property)
"Return comparisons needed by the pre-E4 linear lookup for PROPERTY."
(let ((comparisons 0))
(catch 'found
(dolist (entry ebox-style-schema-test--legacy-nondefault-rules)
(cl-incf comparisons)
(when (eq property (car entry))
(throw 'found comparisons))))
comparisons))
(defun ebox-style-schema-test--legacy-expand-property (property value)
"Expand pre-E4 PROPERTY and VALUE into private engine fields."
(let ((rule (ebox-style-schema-test--legacy-rule property)))
(cond
((null rule) (list property value))
((plist-get rule :expand)
(let* ((targets (plist-get rule :expand))
(parts
(ebox-style--split-value
value (plist-get rule :mode) (length targets))))
(cl-mapcan #'ebox-style-schema-test--legacy-expand-property
targets parts)))
((plist-member rule :to)
(when-let* ((target (plist-get rule :to)))
(list target
(ebox-style--convert value (plist-get rule :conv)))))
(t (list property value)))))
(defun ebox-style-schema-test--legacy-expand-plist (plist)
"Project PLIST through the complete pre-E4 artifact table."
(let (expanded)
(cl-loop for (property value) on plist by #'cddr
do (setq expanded
(nconc
expanded
(ebox-style-schema-test--legacy-expand-property
property value))))
(ebox-style--apply-border-used-widths
(ebox-style--validate-edge-longhands expanded))))
(ert-deftest ebox-style-schema-is-the-only-public-projection-truth ()
"Every public property exposes one schema-owned engine projection."
(should ebox-style--engine-projection-schema-valid-p)
(should-not (boundp 'ebox-style--engine-projection-rules))
(dolist (definition ebox-style--property-definitions)
(let* ((name (plist-get definition :name))
(metadata (ebox-style-property name))
(targets (ebox-style--engine-projection-targets name)))
(should (equal (plist-get metadata :engine-projection)
(plist-get definition :engine-projection)))
(should (equal (plist-get metadata :engine-targets) targets))
(should (equal (plist-get metadata :impacts)
(ebox-style--schema-impacts definition)))
(should (equal (plist-get metadata :projections)
(ebox-style--schema-projections definition)))
(should (plist-get metadata :owner))
(when-let* ((expanded
(plist-get (plist-get definition :engine-projection)
:expand)))
(dolist (target expanded)
(let ((target-definition
(ebox-style--definition-for-name target)))
(should target-definition))))
(dolist (alias (plist-get definition :aliases))
(should (equal (ebox-style--engine-projection-targets alias)
targets))))))
(ert-deftest ebox-style-schema-retains-only-engine-internal-exceptions ()
"Only fields outside the public vocabulary may use the internal rule table."
(should
(equal (mapcar #'car ebox-style--internal-engine-projection-rules)
'(:display)))
(dolist (entry ebox-style--internal-engine-projection-rules)
(should-not (ebox-style--definition-for-name (car entry))))
(should
(equal
(ebox-style-schema-test--sorted-rules
(ebox-style-schema-test--current-explicit-rules))
(ebox-style-schema-test--sorted-rules
ebox-style-schema-test--legacy-nondefault-rules)))
(dolist (entry ebox-style-schema-test--legacy-nondefault-rules)
(let ((name (car entry))
(rule (cdr entry)))
(if (eq name :display)
(should (equal (ebox-style--get-ebox-rule name) rule))
(should (ebox-style--definition-for-name name))
(should (equal (ebox-style--get-ebox-rule name) rule))))))
(ert-deftest ebox-style-schema-preserves-pre-e4-engine-artifacts ()
"Definition-owned projection remains property-for-property equivalent."
(dolist
(fixture
'((:padding (1 (2) 3 (4))
:margin (5 (6) 7 (8))
:padding-inline ((9) (10))
:margin-block (11 12))
(:border (2 solid "#123456")
:border-width (1 2 3 4)
:border-style (solid solid none solid)
:border-color ("#111111" "#222222" "#333333" "#444444"))
(:width (240) :min-width (40) :max-width none
:height 4 :min-height 1 :max-height 8
:outer block :display flex)
(:color "#102030" :background-color "#405060"
:bgcolor "#708090" :text-decoration-color "#A0B0C0"
:text-decoration-line underline :visibility hidden)
(:padding-block-start 2 :padding-inline-end (3)
:padding-block-end 4 :padding-inline-start (5)
:margin-block-start 6 :margin-inline-end (7)
:margin-block-end 8 :margin-inline-start (9))))
(should
(equal (ebox-style--expand-engine-plist
(ebox-test--explicit-sizes 'box fixture))
(ebox-style-schema-test--legacy-expand-plist
(ebox-test--explicit-sizes 'box fixture))))))
(ert-deftest ebox-style-schema-malformed-projections-fail-fast ()
"Ambiguous, missing, or cyclic projection declarations are rejected."
(let ((ambiguous (copy-tree ebox-style--property-definitions))
(missing (copy-tree ebox-style--property-definitions))
(cyclic (copy-tree ebox-style--property-definitions))
(alias-name-collision (copy-tree ebox-style--property-definitions))
(id-name-collision (copy-tree ebox-style--property-definitions))
(alias-id-collision (copy-tree ebox-style--property-definitions)))
(plist-put (car ambiguous) :engine-projection
'(:expand (:color) :mode same :to :color))
(should-error
(ebox-style--validate-engine-projection-definitions ambiguous)
:type 'ebox-style-schema-error)
(plist-put (car missing) :engine-projection
'(:expand (:not-an-ebox-property) :mode same))
(should-error
(ebox-style--validate-engine-projection-definitions missing)
:type 'ebox-style-schema-error)
(plist-put (car cyclic) :engine-projection
'(:expand (:color) :mode same))
(should-error
(ebox-style--validate-engine-projection-definitions cyclic)
:type 'ebox-style-schema-error)
(plist-put (car alias-name-collision) :aliases
(list (plist-get (nth 1 alias-name-collision) :name)))
(should-error
(ebox-style--validate-engine-projection-definitions alias-name-collision)
:type 'ebox-style-schema-error)
(plist-put (nth 1 id-name-collision) :id
(plist-get (car id-name-collision) :name))
(should-error
(ebox-style--validate-engine-projection-definitions id-name-collision)
:type 'ebox-style-schema-error)
(plist-put (car alias-id-collision) :aliases
(list (plist-get (nth 1 alias-id-collision) :id)))
(should-error
(ebox-style--validate-engine-projection-definitions alias-id-collision)
:type 'ebox-style-schema-error)))
(ert-deftest ebox-style-schema-performance-uses-one-lookup-per-property ()
"Schema-owned lookup improves parent linear scans to O(1) per property."
(let ((names
(mapcar (lambda (definition) (plist-get definition :name))
ebox-style--property-definitions))
(lookups 0)
(legacy-comparisons 0)
(original (symbol-function 'ebox-style--property))
benchmark)
(cl-letf (((symbol-function 'ebox-style--property)
(lambda (&rest arguments)
(cl-incf lookups)
(apply original arguments))))
(setq benchmark
(benchmark-run
1
(dotimes (_iteration 100)
(dolist (name names)
(ebox-style--engine-projection-targets name))))))
(dotimes (_iteration 100)
(dolist (name names)
(cl-incf legacy-comparisons
(ebox-style-schema-test--legacy-rule-comparisons name))))
(should (= lookups (* 100 (length names))))
(should (> legacy-comparisons (* 2 lookups)))
(should (< (car benchmark) 2.0))))
(provide 'ebox-style-schema-tests)
;;; ebox-style-schema-tests.el ends here