Skip to content

Commit b586df5

Browse files
committed
Add fuzz test for HTML escape/unescape functions
Introduces a deterministic fuzz test to verify that str.escape_html and str.unescape_html roundtrip correctly and that the escaped output does not contain raw angle brackets or quotes.
1 parent 0596c9e commit b586df5

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import gleeunit
2+
import str
3+
import gleam/list
4+
import gleam/string
5+
6+
pub fn main() -> Nil {
7+
gleeunit.main()
8+
}
9+
10+
// Deterministic, simple generator over a token pool.
11+
fn gen_token_pool() -> List(String) {
12+
[
13+
"a","b","c","1","2","3"," ","\n","<",">","&","\"","'",
14+
"&amp;","&lt;","&gt;","&quot;","&#39;","&#x27;","&#x22;","&notanentity;",
15+
"&","&amp","&#", "&#x",
16+
"\u{00A0}", // NBSP
17+
"Café","naïve","ø","漢","字",
18+
"👩‍👩‍👧‍👦","👨‍👩‍👧","️","✈️","🏳️‍🌈",
19+
"\u{0301}", // combining acute
20+
"&alpha;","&beta;","&gamma;"
21+
]
22+
}
23+
24+
// Deterministic pseudo-random index using seed and i
25+
fn idx_for(seed: Int, i: Int, len: Int) -> Int {
26+
// simple LCG-ish formula; keep small to avoid large-int overhead
27+
let v = seed * 1103515245 + 12345 + i
28+
let v_pos = case v < 0 { True -> -v False -> v }
29+
v_pos % len
30+
}
31+
32+
fn gen_string(seed: Int, tokens: List(String), n: Int) -> String {
33+
let len = list.length(tokens)
34+
let seq = list.range(0, n - 1)
35+
seq
36+
|> list.map(fn(i) {
37+
let j = idx_for(seed, i, len)
38+
case list.drop(tokens, j) {
39+
[first, ..] -> first
40+
[] -> ""
41+
}
42+
})
43+
|> list.fold("", fn(acc, s) { acc <> s })
44+
}
45+
46+
fn run_cfg(seed: Int, n: Int, tokens: List(String)) -> Bool {
47+
let s = gen_string(seed, tokens, n)
48+
// Roundtrip: unescape(escape(s)) == s
49+
let escaped = str.escape_html(s)
50+
let unescaped = str.unescape_html(escaped)
51+
assert unescaped == s
52+
53+
// Escaped string must not contain raw angle brackets or quotes
54+
assert string.contains(escaped, "<") == False
55+
assert string.contains(escaped, ">") == False
56+
assert string.contains(escaped, "\"") == False
57+
assert string.contains(escaped, "'") == False
58+
59+
True
60+
}
61+
62+
pub fn fuzz_roundtrip_test() {
63+
let tokens = gen_token_pool()
64+
65+
run_cfg(1, 20, tokens)
66+
run_cfg(42, 50, tokens)
67+
run_cfg(123, 200, tokens)
68+
69+
True
70+
}

0 commit comments

Comments
 (0)