From ae8b635602eebb9e6ee3c461a2808a04c9803d29 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Tue, 9 Jun 2026 23:35:51 -0400 Subject: [PATCH] =?UTF-8?q?test:=20comprehensive=20spec=20=E2=80=94=20rege?= =?UTF-8?q?x=20+=20sorted=20colls=20+=20random/predicate=20gaps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filling the biggest untested clojure.core areas found in a coverage audit (168 of 506 provided fns had no spec). New + expanded suites: - regex-spec.janet (20): #"…" literals, regex?, re-find/re-matches/re-seq (match/no-match/groups), re-pattern, and clojure.string split/replace with regex (incl $1 backrefs). Whole area was previously unspecced. - sorted-spec.janet (14): sorted-map/sorted-set construction + ordering, sorted?, subseq/rsubseq. Pins the working subset — get/conj/assoc/keys/vals on sorted colls and the by-comparator ctors are not yet first-class (jolt-ti9). - predicates-spec +14: seqable?, integer?, reduced?/unreduced, not-empty. - numbers-spec +5: rand/rand-int/rand-nth invariants. Fix: sorted? was bound to core-sorted-map? so it returned false for sorted-sets; now true for both sorted maps and sets (core-sorted?). Filed: jolt-ti9 (sorted collections incomplete: get/conj/assoc/keys/vals don't operate on the sorted wrapper; sorted-*-by ignore the comparator). Gate green incl full jpm build + jpm test. --- src/jolt/core.janet | 4 +++- test/spec/numbers-spec.janet | 7 +++++++ test/spec/predicates-spec.janet | 16 ++++++++++++++++ test/spec/regex-spec.janet | 33 +++++++++++++++++++++++++++++++++ test/spec/sorted-spec.janet | 29 +++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 test/spec/regex-spec.janet create mode 100644 test/spec/sorted-spec.janet diff --git a/src/jolt/core.janet b/src/jolt/core.janet index b94fd1e..f67d757 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -581,6 +581,8 @@ # ordered by key/element on read. Defined early so seq/count/get can dispatch. (defn core-sorted-map? [x] (and (table? x) (= :jolt/sorted-map (x :jolt/type)))) (defn core-sorted-set? [x] (and (table? x) (= :jolt/sorted-set (x :jolt/type)))) +# sorted? is true for either sorted maps or sorted sets (Clojure: any Sorted coll). +(defn core-sorted? [x] (or (core-sorted-map? x) (core-sorted-set? x))) (defn sm-make [m] @{:jolt/type :jolt/sorted-map :map m}) (defn ss-make [items] @{:jolt/type :jolt/sorted-set :items items}) (defn core-sorted-map [& kvs] @@ -2828,7 +2830,7 @@ "namespace" core-namespace "sorted-map" core-sorted-map "sorted-set" core-sorted-set - "sorted?" core-sorted-map? + "sorted?" core-sorted? "reduced" core-reduced "reduced?" core-reduced? "take-nth" core-take-nth diff --git a/test/spec/numbers-spec.janet b/test/spec/numbers-spec.janet index 54f32cc..ff00ba1 100644 --- a/test/spec/numbers-spec.janet +++ b/test/spec/numbers-spec.janet @@ -126,3 +126,10 @@ ["bit-clear" "13" "(bit-clear 15 1)"] ["bit-test true" "true" "(bit-test 4 2)"] ["bigint 64-bit" "\"9000000000\"" "(str (bigint 9000000000))"]) + +(defspec "numbers / random (invariants — non-deterministic)" + ["rand-int in range" "true" "(let [r (rand-int 5)] (and (integer? r) (>= r 0) (< r 5)))"] + ["rand-int zero" "0" "(rand-int 1)"] + ["rand in [0,1)" "true" "(let [r (rand)] (and (>= r 0) (< r 1)))"] + ["rand-nth member" "true" "(contains? #{:a :b :c} (rand-nth [:a :b :c]))"] + ["rand-nth single" ":x" "(rand-nth [:x])"]) diff --git a/test/spec/predicates-spec.janet b/test/spec/predicates-spec.janet index fd8ea0a..498e643 100644 --- a/test/spec/predicates-spec.janet +++ b/test/spec/predicates-spec.janet @@ -113,3 +113,19 @@ ["not= differs" "true" "(not= [1 2] [1 3])"] ["identical? same kw" "true" "(identical? :a :a)"] ["compare strings" "-1" "(compare \"a\" \"b\")"]) + +(defspec "predicates / seqable, reduced & emptiness" + ["seqable? vector" "true" "(seqable? [1])"] + ["seqable? map" "true" "(seqable? {:a 1})"] + ["seqable? string" "true" "(seqable? \"s\")"] + ["seqable? nil" "true" "(seqable? nil)"] + ["seqable? number" "false" "(seqable? 5)"] + ["integer? int" "true" "(integer? 5)"] + ["integer? fraction" "false" "(integer? 5.5)"] + ["reduced? wrapped" "true" "(reduced? (reduced 1))"] + ["reduced? plain" "false" "(reduced? 1)"] + ["unreduced wrapped" "9" "(unreduced (reduced 9))"] + ["unreduced plain" "9" "(unreduced 9)"] + ["not-empty full" "[1]" "(not-empty [1])"] + ["not-empty empty" "nil" "(not-empty [])"] + ["not-empty string" "nil" "(not-empty \"\")"]) diff --git a/test/spec/regex-spec.janet b/test/spec/regex-spec.janet new file mode 100644 index 0000000..15b3b8e --- /dev/null +++ b/test/spec/regex-spec.janet @@ -0,0 +1,33 @@ +# Specification: regular expressions — #"…" literals and the re-* fns. +# (Whole area previously unspecced; some cases adapted from jank reader-macro/regex.) +(use ../support/harness) + +(defspec "regex / literals & predicate" + ["regex? literal" "true" "(regex? #\"\\d+\")"] + ["regex? non-regex" "false" "(regex? \"\\d+\")"] + ["escaped digits" "\"42\"" "(re-find #\"\\d+\" \"x42y\")"] + ["escaped ws/non-ws" "\"x a\"" "(re-find #\"\\S\\s\\S\" \"x a b y\")"]) + +(defspec "regex / re-find" + ["match" "\"123\"" "(re-find #\"\\d+\" \"abc123def\")"] + ["no match nil" "nil" "(re-find #\"\\d+\" \"abc\")"] + ["with groups" "[\"a1\" \"a\" \"1\"]" "(re-find #\"([a-z])(\\d)\" \"--a1--\")"] + ["first match only" "\"1\"" "(re-find #\"\\d\" \"1 2 3\")"]) + +(defspec "regex / re-matches" + ["full match" "\"123\"" "(re-matches #\"\\d+\" \"123\")"] + ["partial = nil" "nil" "(re-matches #\"\\d+\" \"123abc\")"] + ["groups" "[\"12\" \"1\" \"2\"]" "(re-matches #\"(\\d)(\\d)\" \"12\")"] + ["no match nil" "nil" "(re-matches #\"x+\" \"yyy\")"]) + +(defspec "regex / re-seq" + ["all matches" "(quote (\"1\" \"22\" \"333\"))" "(re-seq #\"\\d+\" \"a1b22c333\")"] + ["empty when none" "nil" "(seq (re-seq #\"z\" \"abc\"))"] + ["words" "(quote (\"foo\" \"bar\"))" "(re-seq #\"\\w+\" \"foo bar\")"]) + +(defspec "regex / re-pattern & string ops" + ["re-pattern build" "\"hi\"" "(re-find (re-pattern \"\\\\w+\") \"hi!\")"] + ["re-pattern is regex?" "true" "(regex? (re-pattern \"a\"))"] + ["split on regex" "[\"a\" \"b\" \"c\"]" "(do (require '[clojure.string :as s]) (s/split \"a1b2c\" #\"\\d\"))"] + ["replace regex" "\"X-X\"" "(do (require '[clojure.string :as s]) (s/replace \"a-b\" #\"[a-z]\" \"X\"))"] + ["replace $1" "\"[a][b]\"" "(do (require '[clojure.string :as s]) (s/replace \"ab\" #\"([a-z])\" \"[$1]\"))"]) diff --git a/test/spec/sorted-spec.janet b/test/spec/sorted-spec.janet new file mode 100644 index 0000000..76608d5 --- /dev/null +++ b/test/spec/sorted-spec.janet @@ -0,0 +1,29 @@ +# Specification: sorted collections (sorted-map / sorted-set, subseq/rsubseq). +# +# NOTE: sorted collections are only partially first-class in Jolt — get/conj/assoc/ +# contains?/keys/vals on a sorted coll, and the by-comparator constructors, are NOT +# yet wired up (jolt-ti9). This spec pins the behavior that DOES work (construction, +# SEQ ordering, subseq/rsubseq, sorted?, first) so it can't regress; the broken ops +# are tracked in jolt-ti9. (vec coerces the seq to a vector so expecteds are vector +# literals rather than quoted lists.) +(use ../support/harness) + +(defspec "sorted / construction & ordering" + ["sorted-set orders" "[1 2 3]" "(vec (seq (sorted-set 3 1 2)))"] + ["sorted-set dedupes" "[1 2 3]" "(vec (seq (sorted-set 3 1 2 1 3)))"] + ["sorted-set numeric" "[1 2 10]" "(vec (seq (sorted-set 10 1 2)))"] + ["sorted-map ordered entries" "[[:a 1] [:b 2] [:c 3]]" "(vec (seq (sorted-map :c 3 :a 1 :b 2)))"] + ["first is min" "1" "(first (sorted-set 5 3 9 1))"]) + +(defspec "sorted / sorted?" + ["sorted-set" "true" "(sorted? (sorted-set 1))"] + ["sorted-map" "true" "(sorted? (sorted-map :a 1))"] + ["plain set" "false" "(sorted? #{1})"] + ["plain map" "false" "(sorted? {:a 1})"] + ["vector" "false" "(sorted? [1 2])"]) + +(defspec "sorted / subseq & rsubseq" + ["subseq >=" "[3 4 5]" "(vec (subseq (sorted-set 1 2 3 4 5) >= 3))"] + ["subseq <" "[1 2]" "(vec (subseq (sorted-set 1 2 3 4 5) < 3))"] + ["subseq range" "[2 3 4]" "(vec (subseq (sorted-set 1 2 3 4 5) > 1 < 5))"] + ["rsubseq <=" "[3 2 1]" "(vec (rsubseq (sorted-set 1 2 3 4 5) <= 3))"])