test: lock in nil-valued map preservation (jolt-c7h)

Add a maps-spec defspec covering nil values through the reader (literals), the
construction path, and the op surface (assoc/merge/merge-with/into/conj/zipmap/
select-keys/get-in/dissoc/reduce-kv, nil keys). Raise the clojure-test-suite
baseline 3919 -> 3926 to guard the assertions the fix unlocked.
This commit is contained in:
Yogthos 2026-06-06 22:35:26 -04:00
parent c43f261993
commit 14d3cb1de4
2 changed files with 35 additions and 4 deletions

View file

@ -27,10 +27,12 @@
# platform gap, not a regression in any previously-working behavior. # platform gap, not a regression in any previously-working behavior.
# Raised 3913 -> 3916 with the staged-bootstrap kernel tier (ns-restore-on-throw # Raised 3913 -> 3916 with the staged-bootstrap kernel tier (ns-restore-on-throw
# + faithful subvec coercion), then 3916 -> 3919 moving juxt/every-pred/some-fn to # + faithful subvec coercion), then 3916 -> 3919 moving juxt/every-pred/some-fn to
# Clojure (the canonical defs are more correct than the prior Janet ones). 3919 is # Clojure (the canonical defs are more correct than the prior Janet ones). Raised
# the stable value; runs can read 3920 when a timeout-prone test (of the 9 that # 3919 -> 3926 preserving nil map values (jolt-c7h): a nil value is a present key,
# can time out) happens to finish, so the floor is set at the consistent 3919. # which several suite tests assert. Runs read 3927 consistently, occasionally 3926
(def baseline-pass 3919) # when a timeout-prone test (of the 9 that can time out) doesn't finish; floor at
# the consistent-minus-one 3926.
(def baseline-pass 3926)
# A file is "clean" when it ran with zero failures AND zero errors. # A file is "clean" when it ran with zero failures AND zero errors.
(def baseline-clean-files 45) (def baseline-clean-files 45)
# Per-file wall-clock budget (seconds). Normal files finish in well under 1s; # Per-file wall-clock budget (seconds). Normal files finish in well under 1s;

View file

@ -115,3 +115,32 @@
["subvec float trunc" "[0]" "(subvec [0 1 2] 0.5 1.33)"] ["subvec float trunc" "[0]" "(subvec [0 1 2] 0.5 1.33)"]
["subvec NaN start" "[0 1 2]" "(subvec [0 1 2] ##NaN 3)"] ["subvec NaN start" "[0 1 2]" "(subvec [0 1 2] ##NaN 3)"]
["subvec NaN end" "[]" "(subvec [0 1 2] 0 ##NaN)"]) ["subvec NaN end" "[]" "(subvec [0 1 2] 0 ##NaN)"])
# A nil value is a PRESENT key in Clojure (distinct from a missing key); Janet
# structs drop nil, so jolt builds these maps as a phm. Tested via literals (the
# reader path) and the construction/op surface, in every spec mode.
(defspec "map / nil values preserved"
["literal contains" "true" "(contains? {:b nil} :b)"]
["literal not= empty" "false" "(= {:b nil} {})"]
["literal get nil" "nil" "(get {:b nil} :b :x)"]
["literal keys incl nil" "true" "(= #{:a :b} (set (keys {:a nil :b 1})))"]
["literal count" "2" "(count {:a nil :b 1})"]
["literal vals incl nil" "2" "(count (vals {:a nil :b 1}))"]
["eval values w/ nil" "3" "(:a {:a (+ 1 2) :b nil})"]
["nil key present" "true" "(contains? {nil :v} nil)"]
["assoc nil present" "true" "(contains? (assoc {:a 1} :b nil) :b)"]
["assoc nil get" "nil" "(get (assoc {:a 1} :b nil) :b :x)"]
["assoc overwrite nil" "nil" "(get (assoc {:a 1} :a nil) :a :x)"]
["hash-map nil" "true" "(contains? (hash-map :b nil) :b)"]
["merge new nil" "true" "(contains? (merge {:a 1} {:b nil}) :b)"]
["merge overwrite nil" "nil" "(get (merge {:a 1} {:a nil}) :a :x)"]
["merge-with present nil" "true" "(= [nil 1] (get (merge-with (fn [a b] [a b]) {:a nil} {:a 1}) :a))"]
["into nil val" "true" "(contains? (into {} [[:a nil]]) :a)"]
["conj map nil" "true" "(contains? (conj {:x 1} {:a nil}) :a)"]
["zipmap nil" "true" "(contains? (zipmap [:a] [nil]) :a)"]
["select-keys nil" "true" "(contains? (select-keys {:a nil} [:a]) :a)"]
["get-in present nil" "nil" "(get-in {:a nil} [:a] :x)"]
["get-in through nil" ":x" "(get-in {:a nil} [:a :b] :x)"]
["dissoc keeps nil" "true" "(contains? (dissoc {:a nil :b 1} :b) :a)"]
["reduce-kv sees nil" "true" "(= #{:a :b} (reduce-kv (fn [acc k v] (conj acc k)) #{} {:a nil :b 2}))"]
["nil-free stays fast" "true" "(= {:a 1 :b 2} {:b 2 :a 1})"])