perf: gate resize and post-resize interactions
This commit is contained in:
parent
2ca24ce74e
commit
4798d76b51
@ -456,14 +456,14 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(defun etaf-research-shelf--filter-view (model)
|
||||
"Return the filter rail for MODEL and inherited filter Context."
|
||||
(etaf-view
|
||||
(box :layout 'column
|
||||
(column
|
||||
:class "research-shelf-filter-rail" :width 'stretch :min-width 0
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(220)
|
||||
:padding '(1 1)
|
||||
:border (etaf-theme-token :line)
|
||||
:color (etaf-theme-token :ink)
|
||||
:bgcolor (etaf-theme-token :panel)
|
||||
(label :text "Library" :face 'bold)
|
||||
(label :text "Library" :font-weight 'bold)
|
||||
(label :text "VIEWS" :color (etaf-theme-token :muted))
|
||||
(etaf-research-shelf-filter-button
|
||||
:filter-key 'all :fallback "All" :ref 'research-shelf-filter-all)
|
||||
@ -492,18 +492,18 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
"Return the reading list for MODEL and static MAIN FORM."
|
||||
(let ((controller (etaf-research-shelf--controller model)))
|
||||
(etaf-view
|
||||
(box :layout 'column
|
||||
(column
|
||||
:class "research-shelf-list" :width 'stretch :min-width 0
|
||||
:flex-grow 4 :flex-shrink 1 :flex-basis '(620)
|
||||
:padding '(1 2)
|
||||
:border (etaf-theme-token :line)
|
||||
:bgcolor (etaf-theme-token :panel)
|
||||
(box :layout 'flex
|
||||
(flex
|
||||
:width 'stretch :flex-wrap 'wrap :align-items 'center :gap '(1 (8))
|
||||
(box :layout 'column
|
||||
(column
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(0) :min-width 0
|
||||
(label :text (etaf-playground-static-value
|
||||
form :title "Reading queue") :face 'bold)
|
||||
form :title "Reading queue") :font-weight 'bold)
|
||||
(label :text (format "%d items · SQLite-backed"
|
||||
(or (etaf-value (etaf-data-total controller)) 0))
|
||||
:color (etaf-theme-token :muted)))
|
||||
@ -539,7 +539,7 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(defun etaf-research-shelf--detail-view (model form)
|
||||
"Return the selected record inspector for MODEL and static FORM."
|
||||
(etaf-view
|
||||
(box :layout 'column
|
||||
(column
|
||||
:class "research-shelf-detail" :width 'stretch :min-width 0
|
||||
:flex-grow 2 :flex-shrink 1 :flex-basis '(340)
|
||||
:padding '(2 2)
|
||||
@ -548,7 +548,7 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(label :text (etaf-playground-static-value form :title
|
||||
"Selected item")
|
||||
:color (etaf-theme-token :accent))
|
||||
(text :face 'bold
|
||||
(text :font-weight 'bold
|
||||
(expr :value
|
||||
(if-let* ((row (etaf-research-shelf--selected model)))
|
||||
(plist-get row :title)
|
||||
@ -560,7 +560,7 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(plist-get row :kind))
|
||||
"Select one row to inspect it.")))
|
||||
(box :height 1)
|
||||
(text :face 'bold
|
||||
(text :font-weight 'bold
|
||||
(expr :value
|
||||
(if-let* ((row (etaf-research-shelf--selected model)))
|
||||
(etaf-research-shelf--status-label (plist-get row :status))
|
||||
@ -598,7 +598,7 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(finished (and row (equal (plist-get row :status) "finished")))
|
||||
(archived (and row (equal (plist-get row :status) "archived"))))
|
||||
(etaf-view
|
||||
(box :layout 'flex
|
||||
(flex
|
||||
:width 'stretch :flex-wrap 'wrap :gap '(1 (6))
|
||||
(button :label "+ 10%" :ref 'research-shelf-progress
|
||||
:variant 'secondary
|
||||
@ -649,21 +649,21 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(header-spec (etaf-inject 'research-shelf-header-spec nil t)))
|
||||
(lambda ()
|
||||
(etaf-view
|
||||
(box :layout 'flex
|
||||
(flex
|
||||
:class "research-shelf-header" :width 'stretch :min-width 0
|
||||
:flex-wrap 'wrap :align-items 'center :gap '(1 (10))
|
||||
(box :layout 'column
|
||||
(column
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(0) :min-width 0
|
||||
(label :text (etaf-playground-static-value
|
||||
header-spec :eyebrow "Research Shelf")
|
||||
:color (etaf-theme-token :accent))
|
||||
(label :text (etaf-playground-static-value
|
||||
root-spec :title "Research Shelf") :face 'bold)
|
||||
root-spec :title "Research Shelf") :font-weight 'bold)
|
||||
(label :text (etaf-playground-static-value
|
||||
root-spec :subtitle
|
||||
"A quiet place for unfinished ideas")
|
||||
:color (etaf-theme-token :muted)))
|
||||
(box :layout 'column :width 'max-content :min-width 0
|
||||
(column :width 'max-content :min-width 0
|
||||
:flex-grow 0 :flex-shrink 0
|
||||
(checkbox :label "Dark" :ref 'research-shelf-theme-toggle
|
||||
:checked (plist-get model :dark)
|
||||
@ -679,10 +679,10 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
(footer-spec (etaf-inject 'research-shelf-footer-spec nil t)))
|
||||
(lambda ()
|
||||
(etaf-view
|
||||
(box :layout 'flex
|
||||
(flex
|
||||
:class "research-shelf-footer" :width 'stretch
|
||||
:flex-wrap 'wrap :gap '(1 (8))
|
||||
(box :layout 'column
|
||||
(column
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(0) :min-width 0
|
||||
(label :text (plist-get model :toast))
|
||||
(label :text (etaf-playground-static-value
|
||||
@ -697,14 +697,14 @@ constructed. When omitted, initialize it for direct programmatic callers."
|
||||
The App supplies `header', `filters', `library', `detail', and `footer'; each
|
||||
slot has a reusable built-in fallback for direct composition."
|
||||
:view
|
||||
(box :layout 'column
|
||||
(column
|
||||
:class "research-shelf-shell" :width '(viewport)
|
||||
:height 'auto :min-height '(viewport-height)
|
||||
:color (etaf-theme-token :ink)
|
||||
:bgcolor (etaf-theme-token :paper)
|
||||
(slot :name 'header
|
||||
(etaf-research-shelf-header))
|
||||
(box :layout 'flex
|
||||
(flex
|
||||
:class "research-shelf-workspace" :width 'stretch
|
||||
:flex-wrap 'wrap :align-items 'stretch :align-content 'start
|
||||
:gap '(1 (6))
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
(defconst etaf-performance-evaluator-sample-count 20)
|
||||
(defconst etaf-performance-evaluator-latency-budget-ms 50.0)
|
||||
(defconst etaf-performance-evaluator-scenario-budgets-ms
|
||||
'(("theme-toggle" :p95 90.0 :max 110.0))
|
||||
'(("theme-toggle" :p95 90.0 :max 110.0)
|
||||
("post-resize-theme-toggle" :p95 90.0 :max 110.0))
|
||||
"User-approved scenario budgets overriding the default latency budget.")
|
||||
(defconst etaf-performance-evaluator-overhead-budget-ms 2.0)
|
||||
(defconst etaf-performance-evaluator-fixture-size 256)
|
||||
@ -342,6 +343,174 @@ never included in SAMPLES. ACTION's result is passed to VERIFY and CLEANUP."
|
||||
failures))
|
||||
(nreverse failures))))
|
||||
|
||||
(defun etaf-performance-evaluator--visible-item-count (buffer)
|
||||
"Return the item count rendered in BUFFER, or nil."
|
||||
(let ((text (etaf-performance-evaluator--text buffer)))
|
||||
(when (string-match "\\([0-9]+\\) items" text)
|
||||
(string-to-number (match-string 1 text)))))
|
||||
|
||||
(defun etaf-performance-evaluator--visible-page (buffer)
|
||||
"Return BUFFER's rendered `(CURRENT . TOTAL)' page pair, or nil."
|
||||
(let ((text (etaf-performance-evaluator--text buffer)))
|
||||
(when (string-match "Page \\([0-9]+\\) / \\([0-9]+\\)" text)
|
||||
(cons (string-to-number (match-string 1 text))
|
||||
(string-to-number (match-string 2 text))))))
|
||||
|
||||
(cl-defun etaf-performance-evaluator--measure-event
|
||||
(label runtime buffer action verify &key setup)
|
||||
"Measure one public event ACTION under LABEL and run VERIFY.
|
||||
ACTION and VERIFY receive the sample index. Every sample must advance the
|
||||
ETAF generation and preserve the mounted surface invariant."
|
||||
(etaf-performance-evaluator--measure
|
||||
label
|
||||
(lambda (index)
|
||||
(let ((before (etaf-runtime-generation runtime)))
|
||||
(list before
|
||||
(progn (funcall action index)
|
||||
(etaf-runtime-generation runtime)))))
|
||||
(lambda (result index)
|
||||
(unless (> (car result) -1)
|
||||
(error "%s: invalid source generation" label))
|
||||
(unless (> (cadr result) (car result))
|
||||
(error "%s: event did not advance the generation" label))
|
||||
(funcall verify index)
|
||||
(etaf-performance-evaluator--surface-invariant runtime buffer label))
|
||||
:setup setup))
|
||||
|
||||
(defun etaf-performance-evaluator--measure-add
|
||||
(label runtime buffer)
|
||||
"Measure the public Add action under LABEL."
|
||||
(let (before-count)
|
||||
(etaf-performance-evaluator--measure-event
|
||||
label runtime buffer
|
||||
(lambda (_index)
|
||||
(setq before-count
|
||||
(etaf-performance-evaluator--visible-item-count buffer))
|
||||
(etaf-dispatch-event runtime 'research-shelf-add 'press))
|
||||
(lambda (_index)
|
||||
(unless (and before-count
|
||||
(= (1+ before-count)
|
||||
(etaf-performance-evaluator--visible-item-count buffer))
|
||||
(etaf-performance-evaluator--visible-match-p
|
||||
buffer "Added to your shelf"))
|
||||
(error "%s: insertion/count/toast invariant failed" label))))))
|
||||
|
||||
(defun etaf-performance-evaluator--measure-reload
|
||||
(label runtime buffer)
|
||||
"Measure the public Reload action under LABEL."
|
||||
(etaf-performance-evaluator--measure-event
|
||||
label runtime buffer
|
||||
(lambda (_index)
|
||||
(etaf-dispatch-event runtime 'research-shelf-reload 'press))
|
||||
(lambda (_index)
|
||||
(unless (etaf-performance-evaluator--visible-match-p
|
||||
buffer "Library reloaded")
|
||||
(error "%s: reload toast invariant failed" label)))))
|
||||
|
||||
(defun etaf-performance-evaluator--measure-viewport-resize
|
||||
(runtime buffer)
|
||||
"Measure alternating wide/compact viewport publications for BUFFER."
|
||||
(let ((target-buffer (get-buffer buffer)))
|
||||
(unless (buffer-live-p target-buffer)
|
||||
(error "Viewport-resize: target buffer is not live"))
|
||||
(etaf-performance-evaluator--measure
|
||||
"viewport-resize"
|
||||
(lambda (index)
|
||||
(let* ((target (if (zerop (% (1+ index) 2)) 720 1413))
|
||||
(report
|
||||
(ebox-rerender-buffer-with-context target-buffer target 62)))
|
||||
(list target report)))
|
||||
(lambda (result _index)
|
||||
(let* ((target (car result))
|
||||
(report (cadr result)))
|
||||
(unless (and (= (plist-get report :target-viewport-width) target)
|
||||
(eq (plist-get report :projection-kind) 'native-frame)
|
||||
(plist-get report :runtime-published)
|
||||
(not (plist-get report :tp-full-root))
|
||||
(not (plist-get report :tp-scope-fallback)))
|
||||
(error "Viewport-resize: retained native publication failed"))
|
||||
(etaf-performance-evaluator--surface-invariant
|
||||
runtime buffer "viewport-resize"))))))
|
||||
|
||||
(defun etaf-performance-evaluator--measure-post-resize-events
|
||||
(runtime buffer)
|
||||
"Return post-resize row/theme/filter/page/Add/Reload measurements."
|
||||
(let (results)
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-event
|
||||
"post-resize-row-selection" runtime buffer
|
||||
(lambda (index)
|
||||
(etaf-performance-evaluator--select-ref
|
||||
runtime (if (zerop (% (1+ index) 2))
|
||||
'research-shelf-row-1
|
||||
'research-shelf-row-2)))
|
||||
(lambda (index)
|
||||
(let ((ref (if (zerop (% (1+ index) 2))
|
||||
'research-shelf-row-1
|
||||
'research-shelf-row-2)))
|
||||
(unless (string-match-p
|
||||
"selected"
|
||||
(or (plist-get
|
||||
(etaf-runtime-host-props-for runtime ref) :class)
|
||||
""))
|
||||
(error "Post-resize row selection invariant failed")))))
|
||||
results)
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-event
|
||||
"post-resize-theme-toggle" runtime buffer
|
||||
(lambda (_index)
|
||||
(etaf-dispatch-event runtime 'research-shelf-theme-toggle 'press))
|
||||
(lambda (_index)
|
||||
(unless (etaf-performance-evaluator--visible-match-p
|
||||
buffer "\\(Dark theme\\|Light theme\\)")
|
||||
(error "Post-resize theme invariant failed"))))
|
||||
results)
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-event
|
||||
"post-resize-filter-query" runtime buffer
|
||||
(lambda (index)
|
||||
(etaf-dispatch-event
|
||||
runtime
|
||||
(if (zerop (% (1+ index) 2))
|
||||
'research-shelf-filter-reading
|
||||
'research-shelf-filter-all)
|
||||
'press))
|
||||
(lambda (index)
|
||||
(unless (etaf-performance-evaluator--visible-match-p
|
||||
buffer
|
||||
(if (zerop (% (1+ index) 2))
|
||||
"Showing Reading"
|
||||
"Showing All"))
|
||||
(error "Post-resize filter invariant failed"))))
|
||||
results)
|
||||
(etaf-dispatch-event runtime 'research-shelf-filter-all 'press)
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-event
|
||||
"post-resize-pagination" runtime buffer
|
||||
(lambda (_index)
|
||||
(pcase-let ((`(,current . ,_total)
|
||||
(etaf-performance-evaluator--visible-page buffer)))
|
||||
(etaf-dispatch-event
|
||||
runtime
|
||||
(if (= current 1)
|
||||
'research-shelf-page-next
|
||||
'research-shelf-page-previous)
|
||||
'press)))
|
||||
(lambda (_index)
|
||||
(unless (etaf-performance-evaluator--visible-page buffer)
|
||||
(error "Post-resize pagination invariant failed"))))
|
||||
results)
|
||||
(when-let* ((page (etaf-performance-evaluator--visible-page buffer)))
|
||||
(unless (= (car page) 1)
|
||||
(etaf-dispatch-event runtime 'research-shelf-page-previous 'press)))
|
||||
(push (etaf-performance-evaluator--measure-add
|
||||
"post-resize-add-reading" runtime buffer)
|
||||
results)
|
||||
(push (etaf-performance-evaluator--measure-reload
|
||||
"post-resize-reload" runtime buffer)
|
||||
results)
|
||||
(nreverse results)))
|
||||
|
||||
(defun etaf-performance-evaluator-run ()
|
||||
"Run the fixed cross-package evaluator and return non-nil on success."
|
||||
(etaf-performance-evaluator--load-workload)
|
||||
@ -502,6 +671,23 @@ never included in SAMPLES. ACTION's result is passed to VERIFY and CLEANUP."
|
||||
runtime buffer "focus-navigation")))
|
||||
results)
|
||||
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-add
|
||||
"add-reading" runtime buffer)
|
||||
results)
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-reload
|
||||
"reload" runtime buffer)
|
||||
results)
|
||||
(push
|
||||
(etaf-performance-evaluator--measure-viewport-resize
|
||||
runtime buffer)
|
||||
results)
|
||||
(dolist (result
|
||||
(etaf-performance-evaluator--measure-post-resize-events
|
||||
runtime buffer))
|
||||
(push result results))
|
||||
|
||||
;; Capture one representative cross-package trace after the latency
|
||||
;; samples. This validates coverage without folding observer work
|
||||
;; into the product latency distribution.
|
||||
@ -517,6 +703,8 @@ never included in SAMPLES. ACTION's result is passed to VERIFY and CLEANUP."
|
||||
(etaf-dispatch-event runtime 'research-shelf-filter-all 'press)
|
||||
(etaf-dispatch-event runtime 'research-shelf-page-next 'press)
|
||||
(etaf-dispatch-event runtime 'research-shelf-page-previous 'press)
|
||||
(etaf-dispatch-event runtime 'research-shelf-add 'press)
|
||||
(etaf-dispatch-event runtime 'research-shelf-reload 'press)
|
||||
(etaf-performance-evaluator--select-ref
|
||||
runtime 'research-shelf-row-2)
|
||||
(etaf-dispatch-event runtime 'research-shelf-progress 'press)
|
||||
|
||||
@ -69,7 +69,9 @@
|
||||
(should-not
|
||||
(etaf-performance-evaluator--latency-failures
|
||||
'(("theme-toggle" :count 20 :min 80.0 :p50 84.0
|
||||
:p95 86.0 :max 99.0))))
|
||||
:p95 86.0 :max 99.0)
|
||||
("post-resize-theme-toggle" :count 20 :min 80.0 :p50 84.0
|
||||
:p95 86.0 :max 99.0))))
|
||||
(should (= 2
|
||||
(length
|
||||
(etaf-performance-evaluator--latency-failures
|
||||
|
||||
Loading…
Reference in New Issue
Block a user