Skip to content

Commit 8a9e492

Browse files
committed
test(suites): convert the regex surface
Smell-audited: 1: routine port on the established path. phase6_regex_cycle1.sh 25 spawns -> suites/regex_test.clj Two of its cases drove `cljw -` with a heredoc rather than `-e`, to assert what `prn` PRINTS for a regex: the `#"…"` reader form from pr/prn (JVM print-method Pattern) versus the raw pattern from str (Pattern.toString). Those are still representation assertions, so they keep pr-str/str rather than becoming value equality. The INV-1 case is kept and labelled: a nested-counted-repetition compile bomb `(a{65535}){65535}`, which would expand to ~4.3e9 instructions, must raise a CATCHABLE IllegalArgumentException rather than OOM or kill the process. The matcher is a Pike NFA and so is ReDoS-immune on untrusted input; this guards the compile side, which is the remaining exposure. The archived ROADMAP names this script as the Phase 6.6 EXIT smoke. That is a dated record of how the phase exited and stays as written; the pins live on in the suite. 25 more spawns leave the gate; 698 for the arc across 20 scripts. Suite tier 25 suites / 208 tests / 1163 assertions / 0 failures. e2e 393 scripts (from 410). Gates: e2e_reach, runner_reach 68 units, e2e_dup, accepted_divergences 60 pinned.
1 parent 4e55cda commit 8a9e492

3 files changed

Lines changed: 68 additions & 130 deletions

File tree

‎test/clj/suites/regex_test.clj‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
;; Regex surface (ADR-0031 cycle 1, capturing groups D-093, §A26 print sweep).
2+
;;
3+
;; The matcher is a Pike NFA, so it is ReDoS-immune on untrusted INPUT; the
4+
;; INV-1 case below guards the remaining exposure, the compile side.
5+
;;
6+
;; Migrated from test/e2e/phase6_regex_cycle1.sh; each assertion keeps its bash
7+
;; case name.
8+
(ns suites.regex-test
9+
(:require [clojure.test :refer [deftest is testing]]))
10+
11+
(deftest re-find-matches-the-leftmost-occurrence
12+
(is (= "123" (re-find #"\d+" "abc123"))) ; cycle1_exit_smoke_re_find_digits
13+
(is (= "b" (re-find #"a|b" "xby"))) ; cycle1_alt
14+
(is (= "aaa" (re-find #"a*" "aaa"))) ; cycle1_star
15+
(is (= "def" (re-find #"[a-z]+" "ABCdef"))) ; cycle1_class_range
16+
(is (= "hello" (re-find #"\w+" " hello world ")))) ; cycle1_word_escape
17+
18+
(deftest re-matches-requires-the-WHOLE-string
19+
(is (= "123" (re-matches #"\d+" "123"))) ; cycle1_re_matches_full
20+
(testing "a partial match is nil, not the matched part"
21+
(is (nil? (re-matches #"\d+" "123abc"))))) ; cycle1_re_matches_partial_nil
22+
23+
(deftest anchors-and-re-pattern
24+
(is (= "abc" (re-find #"^abc$" "abc"))) ; cycle1_anchor_full
25+
(is (nil? (re-find #"^abc$" "xabc"))) ; cycle1_anchor_no_match
26+
(testing "re-pattern builds a regex re-find accepts"
27+
;; the string literal "\\d" decodes to \d, which is what the compiler sees
28+
(is (= "9" (re-find (re-pattern "\\d") "x9y"))))) ; cycle1_re_pattern_round_trip
29+
30+
(deftest re-seq-yields-successive-non-overlapping-matches
31+
(is (= '("1" "22" "333") (re-seq #"\d+" "a1b22c333"))) ; reseq_nums
32+
(is (= '("ab" "cd" "ef") (re-seq #"[a-z]+" "ab cd ef"))) ; reseq_words
33+
(testing "no match is nil, not an empty seq"
34+
(is (nil? (re-seq #"\d+" "abc")))) ; reseq_none
35+
(testing "the re-find-from primitive returns [match start end]"
36+
(is (= ["22" 3 5] (re-find-from #"\d+" "a1b22" 2))))) ; refindfrom
37+
38+
;; D-093: with capturing groups the result is [whole g1 …] instead of the
39+
;; whole-match string
40+
(deftest capturing-groups
41+
(is (= ["ab" "a" "b"] (re-find #"(\w)(\w)" "ab"))) ; group_re_find
42+
(is (= ["42" "4" "2"] (re-matches #"(\d)(\d)" "42"))) ; group_re_matches
43+
(testing "nested groups number outer-then-inner"
44+
(is (= ["42" "42" "4" "2"] (re-find #"((\d)(\d))" "42")))) ; group_nested
45+
(testing "a group that does not participate is nil"
46+
(is (= ["a" "a" nil] (re-find #"(a)(b)?" "a")))) ; group_optional_nil
47+
(testing "(?:…) is non-capturing and skipped in the vector"
48+
(is (= ["ab" "b"] (re-find #"(?:\w)(\w)" "ab")))) ; group_non_capturing
49+
(testing "greedy backtracking still finds the leftmost-greedy submatch"
50+
(is (= ["aaaa" "aaa" "a"] (re-matches #"(a+)(a+)" "aaaa")))) ; group_greedy
51+
(testing "re-seq yields one group vector per match"
52+
(is (= '(["12" "1" "2"] ["34" "3" "4"]) (re-seq #"(\d)(\d)" "1234")))) ; group_re_seq
53+
(testing "no groups still gives the whole-match string"
54+
(is (= "hi" (re-find #"\w+" "hi"))))) ; group_none_string
55+
56+
;; pr/prn render the `#"…"` reader form (JVM print-method Pattern); str renders
57+
;; the raw pattern (Pattern.toString)
58+
(deftest printing-a-regex
59+
(is (= "#\"a.c\"" (pr-str (re-pattern "a.c")))) ; regex_pr_reader_form
60+
(is (= "a.c" (str #"a.c")))) ; regex_str_raw
61+
62+
;; INV-1: a nested-counted-repetition compile bomb on untrusted PATTERN input
63+
;; — `(a{65535}){65535}` would expand to ~4.3e9 instructions — must be a
64+
;; catchable IllegalArgumentException, not an OOM or a process kill.
65+
(deftest a-compile-bomb-is-catchable-not-fatal
66+
(is (= :caught
67+
(try (do (re-pattern "(a{65535}){65535}") :uncaught)
68+
(catch IllegalArgumentException _ :caught))))) ; inv1_compile_bomb_catchable

‎test/e2e/phase6_regex_cycle1.sh‎

Lines changed: 0 additions & 129 deletions
This file was deleted.

‎test/run_all.sh‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,6 @@ run_step "e2e_phase4_cli" "bash test/e2e/phase4_cli.sh"
476476
run_step "e2e_phase4_exit" "bash test/e2e/phase4_exit.sh"
477477
run_step "e2e_phase4_exit_codes" "bash test/e2e/phase4_exit_codes.sh"
478478
run_step "e2e_phase5_exit" "bash test/e2e/phase5_exit.sh"
479-
run_step "e2e_phase6_regex_cycle1" "bash test/e2e/phase6_regex_cycle1.sh"
480479
run_step "e2e_phase6_clojure_string_cycle1" "bash test/e2e/phase6_clojure_string_cycle1.sh"
481480
run_step "e2e_phase6_clojure_string_cycle2" "bash test/e2e/phase6_clojure_string_cycle2.sh"
482481
run_step "e2e_phase6_clojure_string_cycle3" "bash test/e2e/phase6_clojure_string_cycle3.sh"

0 commit comments

Comments
 (0)