refactor: unify style schema truth for M2a E4
Some checks are pending
CI / test (push) Waiting to run
CI / native-build (macos-latest) (push) Waiting to run
CI / native-build (ubuntu-latest) (push) Waiting to run
CI / native-build (windows-latest) (push) Waiting to run
CI / native-msrv (macos-latest) (push) Waiting to run
CI / native-msrv (ubuntu-latest) (push) Waiting to run
CI / native-msrv (windows-latest) (push) Waiting to run

This commit is contained in:
Kinneyzhang 2026-08-31 21:25:19 +08:00
parent a19672a25e
commit 918a88d95d
9 changed files with 550 additions and 154 deletions

View File

@ -10,6 +10,7 @@ Ebox is the low-level spatial rendering engine. Its job is to turn a declarative
- Layout changes are measured before publication; a failed candidate preserves the previous buffer state.
- The native module is an optional accelerator with an exact Elisp fallback.
- New abstractions must remove duplication or make an existing public workflow simpler.
- Public property schema, ECSS metadata, and used engine projection are derived from one property definition record.
## Pipeline

View File

@ -10,6 +10,7 @@ Ebox 是底层空间渲染引擎。它负责把声明式节点树转换为经过
- 布局变化先测量再发布;候选发布失败时保留上一个 buffer 状态。
- native 模块只是可选加速器,必须有完全等价的 Elisp fallback。
- 新抽象必须消除重复,或让已有的公共工作流更简单。
- 公共 property schema、ECSS metadata 与 used engine projection 都从同一 property definition record 派生。
## 流水线

View File

@ -10,7 +10,7 @@ NATIVE_MANIFEST = native/Cargo.toml
NATIVE_TARGET ?= $(shell $(EMACS_BATCH) -l ebox-native-reflow.el --eval '(princ (ebox-native-reflow--rust-target))')
NATIVE_RELEASE_DIR = native/target/$(NATIVE_TARGET)/release
.PHONY: all check ci load compile test checkdoc source-tests font-tests core-tests child-range-tests grid-tests ebox-commit-tests surface-tests visual-check-tests package-tests selector-tests dsl-tests flex-tests state-contract-tests layout-boundary-tests layout-boundary-performance patch-plan-tests patch-plan-performance docs-contract-tests ci-contract-tests performance-evaluator visual-check native-rust-tests native-build diff-check clean package-lint package-lint-install
.PHONY: all check ci load compile test checkdoc source-tests font-tests core-tests child-range-tests grid-tests ebox-commit-tests surface-tests visual-check-tests package-tests selector-tests dsl-tests flex-tests state-contract-tests layout-boundary-tests layout-boundary-performance patch-plan-tests patch-plan-performance style-schema-tests style-schema-performance docs-contract-tests ci-contract-tests performance-evaluator visual-check native-rust-tests native-build diff-check clean package-lint package-lint-install
all: check
@ -25,7 +25,7 @@ compile:
rm -f *.elc tests/*.elc scripts/*.elc
$(EMACS_BATCH) --eval '(setq byte-compile-error-on-warn t byte-compile-warnings (quote (not obsolete)))' -l ebox.el --eval '(ebox-byte-compile)'
test: source-tests font-tests core-tests child-range-tests grid-tests ebox-commit-tests surface-tests visual-check-tests package-tests selector-tests dsl-tests flex-tests state-contract-tests layout-boundary-tests patch-plan-tests docs-contract-tests ci-contract-tests
test: source-tests font-tests core-tests child-range-tests grid-tests ebox-commit-tests surface-tests visual-check-tests package-tests selector-tests dsl-tests flex-tests state-contract-tests layout-boundary-tests patch-plan-tests style-schema-tests docs-contract-tests ci-contract-tests
source-tests:
$(EMACS_BATCH) -l tests/ebox-source-tests.el -f ert-run-tests-batch-and-exit
@ -78,6 +78,12 @@ patch-plan-tests:
patch-plan-performance:
$(EMACS_TEST) -l tests/ebox-patch-plan-tests.el --eval "(ert-run-tests-batch-and-exit 'ebox-patch-plan-performance-bounds-parent-walks)"
style-schema-tests:
$(EMACS_TEST) -l tests/ebox-style-schema-tests.el -f ert-run-tests-batch-and-exit
style-schema-performance:
$(EMACS_TEST) -l tests/ebox-style-schema-tests.el --eval "(ert-run-tests-batch-and-exit 'ebox-style-schema-performance-uses-one-lookup-per-property)"
performance-evaluator: surface-tests ebox-commit-tests
$(EMACS_BATCH) -l scripts/ebox-performance-evaluator.el -f ebox-performance-evaluator-batch

View File

@ -39,7 +39,7 @@ This is the maintainer entry point for the standalone Ebox repository. It descri
The package intentionally does not include application Components, UI controls, reactive data, or a playground implementation. Those are sibling-package responsibilities.
The active contract also covers `Makefile`, `.github/workflows/ci.yml`, `tests/ebox-core-render-tests.el`, `tests/ebox-state-contract-tests.el`, `tests/ebox-layout-boundary-tests.el`, `tests/ebox-patch-plan-tests.el`, `tests/ebox-child-range-tests.el`, `tests/ebox-grid-tests.el`, `tests/ebox-commit-tests.el`, `tests/ebox-surface-tests.el`, `tests/ebox-dsl-tests.el`, `tests/ebox-flex-tests.el`, `tests/ebox-selector-tests.el`, `tests/ebox-package-tests.el`, `tests/ebox-visual-check-tests.el`, `tests/ebox-docs-contract-tests.el`, `tests/ebox-ci-contract-tests.el`, `native/Cargo.toml`, `native/Cargo.lock`, `native/build.rs`, `native/vendor/emacs-30/emacs-module.h`, `native/src/lib.rs`, `native/src/layout.rs`, `native/c/ebox_module.c`, `scripts/ebox-package-lint.el`, `scripts/ebox-visual-check.el`, and `scripts/ebox-performance-evaluator.el`.
The active contract also covers `Makefile`, `.github/workflows/ci.yml`, `tests/ebox-core-render-tests.el`, `tests/ebox-state-contract-tests.el`, `tests/ebox-layout-boundary-tests.el`, `tests/ebox-patch-plan-tests.el`, `tests/ebox-style-schema-tests.el`, `tests/ebox-child-range-tests.el`, `tests/ebox-grid-tests.el`, `tests/ebox-commit-tests.el`, `tests/ebox-surface-tests.el`, `tests/ebox-dsl-tests.el`, `tests/ebox-flex-tests.el`, `tests/ebox-selector-tests.el`, `tests/ebox-package-tests.el`, `tests/ebox-visual-check-tests.el`, `tests/ebox-docs-contract-tests.el`, `tests/ebox-ci-contract-tests.el`, `native/Cargo.toml`, `native/Cargo.lock`, `native/build.rs`, `native/vendor/emacs-30/emacs-module.h`, `native/src/lib.rs`, `native/src/layout.rs`, `native/c/ebox_module.c`, `scripts/ebox-package-lint.el`, `scripts/ebox-visual-check.el`, and `scripts/ebox-performance-evaluator.el`.
## Runtime model
@ -88,6 +88,7 @@ Caller-owned Source Tree
- E1 freezes the closed M2a target classification for retained state: immutable generation facts; generation-bound scroll/native authority; opaque TP client-state custody; one-way compatibility mirrors; and disposable caches. Every inventory row separately records the current v1 storage shape plus its target owner, mutation API, generation binding, rollback, rebuild proof, and cleanup. Current v1 TP client state still holds the whole Ebox state plist until a later checkpoint changes that storage shape. The E1 mirror probes only rebuild and compare projections; they never mutate live state.
- `ebox-layout.el` has no load or call edge to `ebox-surface.el` or TP. Candidate isolation, inherited-cascade detection, and one-shot materialization enter layout only through the validated port owned by `ebox-render-context.el`; `ebox.el` wires the surface implementation after both sides load.
- `ebox-patch-plan.el` consumes only tentative operation plists plus the immutable generation parent table and returns a deterministic plan artifact. The incremental adapter owns all live-fact preparation. E3 defaults to the pure route while retaining explicit `legacy` and equality-checking `shadow` routes; none of the three routes publishes.
- `ebox-style--property-definitions` is also the single used-value projection truth. Non-default engine lowering lives in each property's `:engine-projection`; ECSS metadata exposes the derived `:engine-targets`, and only non-public engine fields may use the small internal exception table.
- `owner-rerender` is broader than `span-patch`, which is broader than `paint-patch`.
- Buffer coordinates belong to the generation that produced them and must be refreshed after mutation.
- Grid uses the normal measurement and rendering pipeline. Native reflow may reject an ineligible tree and must fall back to Elisp without changing correctness.
@ -124,6 +125,8 @@ make layout-boundary-tests
make layout-boundary-performance
make patch-plan-tests
make patch-plan-performance
make style-schema-tests
make style-schema-performance
make docs-contract-tests
make ci-contract-tests
make performance-evaluator

View File

@ -39,7 +39,7 @@
本包有意不包含应用 Component、UI control、响应式 data 或 playground 实现;它们属于同级包。历史应用性能记录器和 native reflow 评估器也不属于独立 Ebox 的发布边界Ebox 只保留 native 模块本身、Rust 构建输入和可重复的构建检查。
active 合同还覆盖 `Makefile`、`.github/workflows/ci.yml`、`tests/ebox-core-render-tests.el`、`tests/ebox-state-contract-tests.el`、`tests/ebox-layout-boundary-tests.el`、`tests/ebox-patch-plan-tests.el`、`tests/ebox-child-range-tests.el`、`tests/ebox-grid-tests.el`、`tests/ebox-commit-tests.el`、`tests/ebox-surface-tests.el`、`tests/ebox-dsl-tests.el`、`tests/ebox-flex-tests.el`、`tests/ebox-selector-tests.el`、`tests/ebox-package-tests.el`、`tests/ebox-visual-check-tests.el`、`tests/ebox-docs-contract-tests.el`、`tests/ebox-ci-contract-tests.el`、`native/Cargo.toml`、`native/Cargo.lock`、`native/build.rs`、`native/vendor/emacs-30/emacs-module.h`、`native/src/lib.rs`、`native/src/layout.rs`、`native/c/ebox_module.c`、`scripts/ebox-package-lint.el`、`scripts/ebox-visual-check.el` 和 `scripts/ebox-performance-evaluator.el`
active 合同还覆盖 `Makefile`、`.github/workflows/ci.yml`、`tests/ebox-core-render-tests.el`、`tests/ebox-state-contract-tests.el`、`tests/ebox-layout-boundary-tests.el`、`tests/ebox-patch-plan-tests.el`、`tests/ebox-style-schema-tests.el`、`tests/ebox-child-range-tests.el`、`tests/ebox-grid-tests.el`、`tests/ebox-commit-tests.el`、`tests/ebox-surface-tests.el`、`tests/ebox-dsl-tests.el`、`tests/ebox-flex-tests.el`、`tests/ebox-selector-tests.el`、`tests/ebox-package-tests.el`、`tests/ebox-visual-check-tests.el`、`tests/ebox-docs-contract-tests.el`、`tests/ebox-ci-contract-tests.el`、`native/Cargo.toml`、`native/Cargo.lock`、`native/build.rs`、`native/vendor/emacs-30/emacs-module.h`、`native/src/lib.rs`、`native/src/layout.rs`、`native/c/ebox_module.c`、`scripts/ebox-package-lint.el`、`scripts/ebox-visual-check.el` 和 `scripts/ebox-performance-evaluator.el`
## 运行时模型
@ -88,6 +88,7 @@ active 合同还覆盖 `Makefile`、`.github/workflows/ci.yml`、`tests/ebox-cor
- E1 固化封闭的 M2a 目标分类:不可变 generation fact、绑定 generation 的 scroll/native authority、opaque TP client-state custody、单向 compatibility mirror以及 disposable cache。每一行分别记录当前 v1 存储形态,以及目标 owner、mutation API、generation binding、rollback、rebuild proof 与 cleanup。在后续 checkpoint 改变存储形态前,当前 v1 TP client state 仍保存完整 Ebox state plist。E1 mirror 探针只重建并比较投影,不修改 live state。
- `ebox-layout.el` 不再 load 或调用 `ebox-surface.el`/TP。candidate isolation、继承 cascade 判定和一次性 materialization 只能通过 `ebox-render-context.el` 拥有的已校验 port 进入 layout`ebox.el` 在两侧加载后接线 surface 实现。
- `ebox-patch-plan.el` 只消费 tentative operation plist 与不可变 generation parent table并返回 deterministic plan artifact所有 live fact 准备仍由 incremental adapter 拥有。E3 默认使用 pure route同时保留显式 `legacy` 与执行等价检查的 `shadow` route三条 route 都不能发布。
- `ebox-style--property-definitions` 同时也是 used-value projection 的唯一真相源。非默认 engine lowering 写在每个 property 的 `:engine-projection`ECSS metadata 暴露派生的 `:engine-targets`,只有非公共 engine 字段可以进入小型 internal exception table。
- `owner-rerender` 范围大于 `span-patch``span-patch` 大于 `paint-patch`
- Buffer 坐标属于生成它的 generation变更后必须重新获取。
- Grid 使用普通测量与渲染流水线native reflow 可以拒绝不适合的树并回退到 Elisp正确性不变。
@ -122,6 +123,8 @@ make layout-boundary-tests
make layout-boundary-performance
make patch-plan-tests
make patch-plan-performance
make style-schema-tests
make style-schema-performance
make docs-contract-tests
make ci-contract-tests
make performance-evaluator

View File

@ -45,10 +45,12 @@
(defconst ebox-style--property-definitions
'((:name :color :id ebox/color :initial nil :inherits t
:contexts (text box) :group paint :dirty-kind paint :signature paint
:normalizer color :validator color)
:normalizer color :validator color
:engine-projection (:to :color :conv color))
(:name :background-color :id ebox/background-color :aliases (:bgcolor)
:initial nil :contexts (text box) :group paint :dirty-kind paint
:signature paint :normalizer color :validator color)
:signature paint :normalizer color :validator color
:engine-projection (:to :bgcolor :conv color))
(:name :font-family :id ebox/font-family :initial nil :inherits t
:contexts (text box) :group typography :dirty-kind geometry :signature layout
:validator font-family)
@ -67,7 +69,8 @@
:validator text-decoration-line)
(:name :text-decoration-color :id ebox/text-decoration-color
:initial currentColor :contexts (text box) :group paint :dirty-kind paint
:signature paint :normalizer color :validator color)
:signature paint :normalizer color :validator color
:engine-projection (:to :text-decoration-color :conv color))
(:name :text-decoration-style :id ebox/text-decoration-style :initial solid
:contexts (text box) :group paint :dirty-kind paint :signature paint
:validator text-decoration-style)
@ -77,15 +80,18 @@
(:name :width :id ebox/width :initial auto
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator horizontal-size)
:validator horizontal-size
:engine-projection (:to :width :conv preferred-size))
(:name :min-width :id ebox/min-width :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator min-horizontal-size)
:validator min-horizontal-size
:engine-projection (:to :min-width :conv min-size))
(:name :max-width :id ebox/max-width :initial none
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator max-horizontal-size)
:validator max-horizontal-size
:engine-projection (:to :max-width :conv max-size))
(:name :height :id ebox/height :initial auto
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
@ -101,83 +107,103 @@
(:name :padding-top :id ebox/padding-top :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-top-height :conv line))
(:name :padding-right :id ebox/padding-right :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-right-pixel :conv pixel))
(:name :padding-bottom :id ebox/padding-bottom :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-bottom-height :conv line))
(:name :padding-left :id ebox/padding-left :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-left-pixel :conv pixel))
(:name :padding-block-start :id ebox/padding-block-start :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-top-height :conv line))
(:name :padding-inline-end :id ebox/padding-inline-end :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-right-pixel :conv pixel))
(:name :padding-block-end :id ebox/padding-block-end :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-bottom-height :conv line))
(:name :padding-inline-start :id ebox/padding-inline-start :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :padding-left-pixel :conv pixel))
(:name :margin-top :id ebox/margin-top :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-top-height :conv line))
(:name :margin-right :id ebox/margin-right :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-right-pixel :conv pixel))
(:name :margin-bottom :id ebox/margin-bottom :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-bottom-height :conv line))
(:name :margin-left :id ebox/margin-left :initial 0
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-left-pixel :conv pixel))
(:name :margin-block-start :id ebox/margin-block-start :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-top-height :conv line))
(:name :margin-inline-end :id ebox/margin-inline-end :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-right-pixel :conv pixel))
(:name :margin-block-end :id ebox/margin-block-end :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-bottom-height :conv line))
(:name :margin-inline-start :id ebox/margin-inline-start :initial 0
:contexts (box row column flex grid) :group geometry
:dirty-kind geometry :signature layout
:validator nonnegative-size)
:validator nonnegative-size
:engine-projection (:to :margin-left-pixel :conv pixel))
(:name :border-top-width :id ebox/border-top-width
:initial 0 :contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator border-width)
:validator border-width
:engine-projection (:to :border-top-pixel :conv border-pixel))
(:name :border-right-width :id ebox/border-right-width
:initial 0 :contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator border-width)
:validator border-width
:engine-projection (:to :border-right-pixel :conv border-pixel))
(:name :border-bottom-width :id ebox/border-bottom-width
:initial 0 :contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator border-width)
:validator border-width
:engine-projection (:to :border-bottom-pixel :conv border-pixel))
(:name :border-left-width :id ebox/border-left-width
:initial 0 :contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout
:validator border-width)
:validator border-width
:engine-projection (:to :border-left-pixel :conv border-pixel))
(:name :border-top-style :id ebox/border-top-style :initial none
:contexts (box row column flex grid) :group structure
:dirty-kind structure :signature layout :validator border-style)
@ -193,19 +219,23 @@
(:name :border-top-color :id ebox/border-top-color :initial currentColor
:contexts (box row column flex grid) :group paint
:dirty-kind paint :signature paint
:normalizer color :validator color)
:normalizer color :validator color
:engine-projection (:to :border-top-color :conv color))
(:name :border-right-color :id ebox/border-right-color :initial currentColor
:contexts (box row column flex grid) :group paint
:dirty-kind paint :signature paint
:normalizer color :validator color)
:normalizer color :validator color
:engine-projection (:to :border-right-color :conv color))
(:name :border-bottom-color :id ebox/border-bottom-color
:initial currentColor :contexts (box row column flex grid)
:group paint :dirty-kind paint :signature paint
:normalizer color :validator color)
:normalizer color :validator color
:engine-projection (:to :border-bottom-color :conv color))
(:name :border-left-color :id ebox/border-left-color :initial currentColor
:contexts (box row column flex grid) :group paint
:dirty-kind paint :signature paint
:normalizer color :validator color)
:normalizer color :validator color
:engine-projection (:to :border-left-color :conv color))
(:name :text-align :id ebox/text-align :initial left :inherits t
:contexts (box)
:author-contexts (box)
@ -225,7 +255,8 @@
:contexts (box row column flex grid)
:group structure :dirty-kind structure :signature structure
:validator outer :impacts (structure geometry)
:projections (structure layout))
:projections (structure layout)
:engine-projection (:to nil))
(:name :item-gap :id ebox/item-gap :initial 0 :contexts (row column)
:group geometry :dirty-kind geometry :signature layout
:validator nonnegative-integer)
@ -302,37 +333,62 @@
:contexts (item) :group geometry :dirty-kind geometry :signature layout)
(:name :padding :id ebox/padding :shorthand padding
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection
(:expand (:padding-top :padding-right :padding-bottom :padding-left)
:mode trbl))
(:name :padding-inline :id ebox/padding-inline :shorthand padding-inline
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection (:expand (:padding-left :padding-right) :mode pair))
(:name :padding-block :id ebox/padding-block :shorthand padding-block
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection (:expand (:padding-top :padding-bottom) :mode pair))
(:name :margin :id ebox/margin :shorthand margin
:contexts (box row column flex grid)
:impacts (geometry) :projections (layout))
:impacts (geometry) :projections (layout)
:engine-projection
(:expand (:margin-top :margin-right :margin-bottom :margin-left)
:mode trbl))
(:name :margin-inline :id ebox/margin-inline :shorthand margin-inline
:contexts (box row column flex grid)
:impacts (geometry) :projections (layout))
:impacts (geometry) :projections (layout)
:engine-projection (:expand (:margin-left :margin-right) :mode pair))
(:name :margin-block :id ebox/margin-block :shorthand margin-block
:contexts (box row column flex grid)
:impacts (geometry) :projections (layout))
:impacts (geometry) :projections (layout)
:engine-projection (:expand (:margin-top :margin-bottom) :mode pair))
(:name :border :id ebox/border :shorthand border
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection
(:expand (:border-top :border-right :border-bottom :border-left)
:mode same))
(:name :border-top :id ebox/border-top :shorthand border-top
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection
(:expand (:border-top-width :border-top-style :border-top-color)
:mode wsc))
(:name :border-right :id ebox/border-right :shorthand border-right
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection
(:expand (:border-right-width :border-right-style :border-right-color)
:mode wsc))
(:name :border-bottom :id ebox/border-bottom :shorthand border-bottom
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection
(:expand (:border-bottom-width :border-bottom-style :border-bottom-color)
:mode wsc))
(:name :border-left :id ebox/border-left :shorthand border-left
:contexts (box row column flex grid)
:impacts (geometry paint) :projections (layout paint))
:impacts (geometry paint) :projections (layout paint)
:engine-projection
(:expand (:border-left-width :border-left-style :border-left-color)
:mode wsc))
(:name :border-top-p :id ebox/border-top-p
:shorthand border-top-present :validator boolean
:contexts (box row column flex grid)
@ -340,13 +396,25 @@
:group structure :dirty-kind structure :signature layout)
(:name :border-width :id ebox/border-width :shorthand border-width
:contexts (box row column flex grid)
:group geometry :dirty-kind geometry :signature layout)
:group geometry :dirty-kind geometry :signature layout
:engine-projection
(:expand (:border-top-width :border-right-width
:border-bottom-width :border-left-width)
:mode trbl))
(:name :border-style :id ebox/border-style :shorthand border-style
:contexts (box row column flex grid)
:group structure :dirty-kind structure :signature layout)
:group structure :dirty-kind structure :signature layout
:engine-projection
(:expand (:border-top-style :border-right-style
:border-bottom-style :border-left-style)
:mode trbl))
(:name :border-color :id ebox/border-color :shorthand border-color
:contexts (box row column flex grid)
:group paint :dirty-kind paint :signature paint)
:group paint :dirty-kind paint :signature paint
:engine-projection
(:expand (:border-top-color :border-right-color
:border-bottom-color :border-left-color)
:mode trbl))
(:name :gap :id ebox/gap :shorthand gap
:contexts (flex grid)
:group geometry :dirty-kind geometry :signature layout)
@ -358,6 +426,118 @@
:group geometry :dirty-kind geometry :signature layout))
"Canonical Ebox property schemas and public aliases.")
(defconst ebox-style--internal-engine-projection-rules
'((:display :to nil))
"Projection rules for engine-only fields outside the public schema.")
(defconst ebox-style--engine-projection-modes '(trbl pair wsc same)
"Closed set of shorthand split modes used by engine projection.")
(defconst ebox-style--engine-projection-converters
'(pixel size-pixel border-pixel preferred-size min-size max-size line color)
"Closed set of value converters used by engine projection.")
(define-error 'ebox-style-schema-error "Invalid Ebox style schema")
(defun ebox-style--definition-for-name (name &optional definitions)
"Return NAME's canonical entry from DEFINITIONS or the package definitions."
(cl-find-if
(lambda (property)
(or (eq name (plist-get property :name))
(eq name (plist-get property :id))
(memq name (plist-get property :aliases))))
(or definitions ebox-style--property-definitions)))
(defun ebox-style--definition-engine-targets
(property &optional definitions stack)
"Return private engine targets derived from PROPERTY in DEFINITIONS."
(let* ((definitions (or definitions ebox-style--property-definitions))
(name (plist-get property :name))
(rule (plist-get property :engine-projection)))
(when (memq name stack)
(signal 'ebox-style-schema-error
(list :engine-projection-cycle (reverse (cons name stack)))))
(cond
((null rule) (list name))
((plist-get rule :expand)
(delete-dups
(cl-mapcan
(lambda (target)
(let ((definition
(ebox-style--definition-for-name target definitions)))
(unless definition
(signal 'ebox-style-schema-error
(list :unknown-engine-target target :property name)))
(ebox-style--definition-engine-targets
definition definitions (cons name stack))))
(plist-get rule :expand))))
((plist-member rule :to)
(when-let* ((target (plist-get rule :to))) (list target)))
(t (list name)))))
(defun ebox-style--validate-engine-projection-definitions (definitions)
"Validate engine projection metadata in DEFINITIONS and return t."
(let ((lookup-keys (make-hash-table :test #'eq)))
(dolist (property definitions)
(let ((name (plist-get property :name))
(id (plist-get property :id))
(rule (plist-get property :engine-projection)))
(unless (and (keywordp name) (symbolp id))
(signal 'ebox-style-schema-error
(list :malformed-property-identity property)))
(dolist (entry
(append (list (cons name :name) (cons id :id))
(mapcar (lambda (alias) (cons alias :alias))
(plist-get property :aliases))))
(unless (symbolp (car entry))
(signal 'ebox-style-schema-error
(list :malformed-lookup-key entry :property name)))
(when-let* ((existing (gethash (car entry) lookup-keys)))
(signal 'ebox-style-schema-error
(list :lookup-key-collision (car entry)
:existing existing
:incoming (list :property name :role (cdr entry)))))
(puthash (car entry)
(list :property name :role (cdr entry))
lookup-keys))
(when rule
(unless (proper-list-p rule)
(signal 'ebox-style-schema-error
(list :malformed-engine-rule name rule)))
(let ((expand (plist-get rule :expand))
(to-present (plist-member rule :to))
(converter (plist-get rule :conv)))
(when (eq (not (null expand)) (not (null to-present)))
(signal 'ebox-style-schema-error
(list :ambiguous-engine-rule name rule)))
(when expand
(unless (and (proper-list-p expand) expand
(memq (plist-get rule :mode)
ebox-style--engine-projection-modes))
(signal 'ebox-style-schema-error
(list :malformed-engine-expansion name rule))))
(when (and to-present
(plist-get rule :to)
(not (keywordp (plist-get rule :to))))
(signal 'ebox-style-schema-error
(list :malformed-engine-target name rule)))
(when (and converter
(not (memq converter
ebox-style--engine-projection-converters)))
(signal 'ebox-style-schema-error
(list :unknown-engine-converter name converter))))))
(ebox-style--definition-engine-targets property definitions))
(dolist (entry ebox-style--internal-engine-projection-rules)
(when (ebox-style--definition-for-name (car entry) definitions)
(signal 'ebox-style-schema-error
(list :public-rule-in-internal-table (car entry)))))
t))
(defconst ebox-style--engine-projection-schema-valid-p
(ebox-style--validate-engine-projection-definitions
ebox-style--property-definitions)
"Non-nil after the package engine projection schema validates.")
(defvar ebox-style-schemas)
(defvar ebox-style--property-index)
@ -1035,6 +1215,9 @@ generated wrappers that already contain exact line breaks."
(setq metadata
(plist-put metadata :impacts
(ebox-style--schema-impacts property)))
(setq metadata
(plist-put metadata :engine-targets
(ebox-style--definition-engine-targets property)))
(plist-put metadata :projections
(ebox-style--schema-projections property))))
@ -2143,98 +2326,6 @@ owned by NEW; only fields owned by the unchanged computed style are shared."
(list (car entry) (cdr entry)))
entries))))
(defconst ebox-style--engine-projection-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-style :to :border-top-style)
(:border-top-color :to :border-top-color :conv color)
(:border-bottom-width :to :border-bottom-pixel :conv border-pixel)
(:border-bottom-style :to :border-bottom-style)
(:border-bottom-color :to :border-bottom-color :conv color)
(:border-left-width :to :border-left-pixel :conv border-pixel)
(:border-left-style :to :border-left-style)
(:border-left-color :to :border-left-color :conv color)
(:border-right-width :to :border-right-pixel :conv border-pixel)
(:border-right-style :to :border-right-style)
(: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)
(:height :to :height)
(:min-height :to :min-height)
(:max-height :to :max-height)
(:outer :to nil)
(:content :to :content)
(:box-sizing :to :box-sizing)
(:color :to :color :conv color)
(:background-color :to :bgcolor :conv color)
(:bgcolor :to :bgcolor :conv color)
(:text-decoration-line :to :text-decoration-line)
(:text-decoration-color :to :text-decoration-color :conv color)
(:text-decoration-style :to :text-decoration-style)
(:text-align :to :text-align)
(:overflow :to :overflow)
(:wrap-mode :to :wrap-mode)
(:display :to nil)
(:visibility :to :visibility))
"Canonical property projections into private runtime storage keys.")
(defun ebox-style--convert (value converter)
"Convert VALUE using Ebox engine CONVERTER."
(pcase converter
@ -2307,7 +2398,9 @@ used geometry and become zero when the corresponding style is not `solid'."
(defun ebox-style--get-ebox-rule (property)
"Return Ebox private engine projection rule for PROPERTY."
(cdr (assq property ebox-style--engine-projection-rules)))
(or (when-let* ((definition (ebox-style--property property)))
(plist-get definition :engine-projection))
(cdr (assq property ebox-style--internal-engine-projection-rules))))
(defun ebox-style--expand-engine-property (property value)
"Expand public Ebox PROPERTY with VALUE to engine longhands."
@ -2329,18 +2422,13 @@ used geometry and become zero when the corresponding style is not `solid'."
(defun ebox-style--engine-projection-targets (property)
"Return private engine keys potentially projected from PROPERTY."
(let* ((name (or (ebox-style-canonical-name property) property))
(rule (ebox-style--get-ebox-rule name)))
(cond
((null rule) (list name))
((plist-get rule :expand)
(delete-dups
(cl-mapcan #'ebox-style--engine-projection-targets
(plist-get rule :expand))))
((plist-member rule :to)
(when-let* ((target (plist-get rule :to)))
(list target)))
(t (list name)))))
(if-let* ((definition (ebox-style--property property)))
(copy-sequence (plist-get definition :engine-targets))
(let ((rule
(cdr (assq property ebox-style--internal-engine-projection-rules))))
(if (and rule (plist-member rule :to))
(when-let* ((target (plist-get rule :to))) (list target))
(list property)))))
(defun ebox-style--expand-engine-plist (plist)
"Project canonical PLIST into private Ebox runtime fields."

View File

@ -23,6 +23,8 @@
"layout-boundary-performance:"
"patch-plan-tests:"
"patch-plan-performance:"
"style-schema-tests:"
"style-schema-performance:"
"docs-contract-tests:"
"ci-contract-tests:" "native-rust-tests:"
"native-build:" "performance-evaluator:" "diff-check:"))

View File

@ -34,6 +34,7 @@
"tests/ebox-core-render-tests.el" "tests/ebox-state-contract-tests.el"
"tests/ebox-layout-boundary-tests.el"
"tests/ebox-patch-plan-tests.el"
"tests/ebox-style-schema-tests.el"
"tests/ebox-child-range-tests.el" "tests/ebox-grid-tests.el"
"tests/ebox-commit-tests.el" "tests/ebox-surface-tests.el"
"tests/ebox-dsl-tests.el" "tests/ebox-flex-tests.el"
@ -53,6 +54,7 @@
"selector-tests" "dsl-tests" "flex-tests" "state-contract-tests"
"layout-boundary-tests" "layout-boundary-performance"
"patch-plan-tests" "patch-plan-performance"
"style-schema-tests" "style-schema-performance"
"docs-contract-tests" "ci-contract-tests" "performance-evaluator"
"visual-check"
"native-rust-tests" "native-build" "package-lint" "diff-check")

View File

@ -0,0 +1,290 @@
;;; 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 (copy-tree fixture))
(ebox-style-schema-test--legacy-expand-plist
(copy-tree 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