mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 14:30:37 +00:00
🐛 Keep whole-program component failure signatures stable
Clear the current tuple after final settlement and before whole-program history checks. A skipped tail must not supply the action kind for a final undo or redo failure. Keep the full labelled trace and the invariant/phase in the error report; preserve immediate and deferred action attribution. Add real passing controls and final undo/redo corruption regressions for local, linked-library and source-editor histories. The regression failed 24 of 84 assertions before the fix. Five focused tests now pass all 121 assertions without weakening their checks. The shrink filter stays intact. The change covers only the generator harness and its tests. Validation: common JVM 2,015 tests / 33,268 assertions; common JS 1,951 / 31,879; frontend 1,751 / 13,976; backend 1,793 / 9,007; exporter 16 / 40. Six properties pass 40 programs at seed 712 in both common runtimes. Lint and format checks pass for all four modules. AI-assisted-by: gpt-6
This commit is contained in:
parent
1a8013f2f6
commit
fcac23c52f
@ -974,7 +974,8 @@
|
||||
|
||||
(defn run
|
||||
"Run a program. A supplied trace must replay the same targets and skips.
|
||||
Failures include the program and labelled trace for a regression."
|
||||
Failures include the program and labelled trace for a regression.
|
||||
Whole-program checks have no single failing operation."
|
||||
([program] (run program nil))
|
||||
([{:keys [prefix ops] world-spec :world :as program} replay-trace]
|
||||
(let [progress (atom (initial-state world-spec))]
|
||||
@ -984,6 +985,7 @@
|
||||
(swap! progress assoc :current tuple :phase phase)
|
||||
(swap! progress step tuple))
|
||||
(swap! progress settle-state)
|
||||
(swap! progress dissoc :current)
|
||||
(check-sequence @progress)
|
||||
(check-source-sequences (:situation @progress))
|
||||
(when-not (pos? (reduce + 0 (vals (dissoc (:executed @progress) :sync))))
|
||||
|
||||
@ -29,6 +29,55 @@
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(t/deftest whole-program-failures-keep-the-same-signature-without-a-skipped-tail
|
||||
(doseq [[world ops source?]
|
||||
[[[1 0] [[:edit 0 0 1 1] [:edit 0 1 1 1]] false]
|
||||
[[1 0 {:library true}] [[:edit 0 0 1 1] [:edit 0 1 1 1]] false]
|
||||
[[1 0 {:library true :stale-libraries true}]
|
||||
[[:library-edit 0 0 1] [:library-edit 0 1 1]] true]]
|
||||
direction [:undo :redo]]
|
||||
(let [short-program {:world world :ops ops}
|
||||
long-program (update short-program :ops conj [:restore 0])
|
||||
phase (keyword (str (when source? "source-") "sequence-" (name direction)))
|
||||
step r/step
|
||||
previous (atom nil)
|
||||
corrupt (fn [situation]
|
||||
(let [file (tm/file situation)
|
||||
page (thf/current-page file)
|
||||
id (get-in (first (filter #(get-in % [:shape :main-instance])
|
||||
(cg/tree situation))) [:shape :id])
|
||||
changes (cls/generate-update-shapes
|
||||
(pcb/empty-changes nil (:id page)) [id]
|
||||
#(assoc % :opacity 0.375) (:objects page) {:ignore-touched true})]
|
||||
(tm/with-file situation (thf/apply-changes file changes))))
|
||||
failure (fn [run]
|
||||
(try (reset! previous nil) (run) nil
|
||||
(catch #?(:clj Exception :cljs js/Error) error
|
||||
(select-keys (ex-data error) [:invariant :phase :failing-op :trace]))))
|
||||
coverage (atom {})
|
||||
signature (atom nil)
|
||||
real (cg/run long-program)]
|
||||
(t/is (map? (cg/run short-program)) "Real whole-program undo/redo passes")
|
||||
(t/is (= 2 (get-in real [:executed (if source? :library-edit :edit)])))
|
||||
(t/is (= :skip (:status (peek (:trace real)))))
|
||||
(with-redefs [r/step (fn [situation op]
|
||||
(let [after (step situation op)
|
||||
kind (n/op-kind op)
|
||||
prior @previous]
|
||||
(reset! previous kind)
|
||||
(if (= direction prior kind) (corrupt after) after)))]
|
||||
(doseq [program [long-program short-program]]
|
||||
(let [data (failure #(cg/run program))]
|
||||
(t/is (= :I7 (:invariant data)))
|
||||
(t/is (= phase (:phase data)))
|
||||
(t/is (= [:executed :executed]
|
||||
(mapv :status (take 2 (:trace data)))))
|
||||
(t/is (nil? (:failing-op data)) "The failure belongs to the whole history")))
|
||||
(t/is (some? (failure #(cg/check-program long-program coverage signature))))
|
||||
(t/is (= [:I7 nil phase nil] @signature))
|
||||
(t/is (some? (failure #(cg/check-program short-program coverage signature)))
|
||||
"Removing the skipped tail must preserve the whole-program failure")))))
|
||||
|
||||
(t/deftest deferred-failures-name-the-executed-action-and-keep-the-shrink-signature
|
||||
(let [short-program {:world [1 0] :ops [[:edit 0 0 1 1] [:update-main 0]]}
|
||||
long-program (update short-program :ops conj [:style 0 3 1 1])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user