package rext import ( "git.blackforestbytes.com/BlackForestBytes/goext/tst" "regexp" "strings" "testing" ) func TestW(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) tst.AssertTrue(t, r != nil) tst.AssertEqual(t, r.String(), `\d+`) } func TestIsMatchTrue(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) tst.AssertTrue(t, r.IsMatch("abc 123 def")) } func TestIsMatchFalse(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) tst.AssertFalse(t, r.IsMatch("abc def")) } func TestString(t *testing.T) { r := W(regexp.MustCompile(`^foo(bar)?$`)) tst.AssertEqual(t, r.String(), `^foo(bar)?$`) } func TestGroupCountWrapper(t *testing.T) { r0 := W(regexp.MustCompile(`abc`)) tst.AssertEqual(t, r0.GroupCount(), 0) r1 := W(regexp.MustCompile(`(a)(b)(c)`)) tst.AssertEqual(t, r1.GroupCount(), 3) r2 := W(regexp.MustCompile(`(?P\d+)-(?P\d+)`)) tst.AssertEqual(t, r2.GroupCount(), 2) } func TestMatchFirstFound(t *testing.T) { r := W(regexp.MustCompile(`(\d+)-(\d+)`)) m, ok := r.MatchFirst("a 12-34 b 56-78 c") tst.AssertTrue(t, ok) tst.AssertEqual(t, m.FullMatch().Value(), "12-34") tst.AssertEqual(t, m.GroupByIndex(1).Value(), "12") tst.AssertEqual(t, m.GroupByIndex(2).Value(), "34") } func TestMatchFirstNotFound(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) m, ok := r.MatchFirst("nothing here") tst.AssertFalse(t, ok) // zero-value match should be returned tst.AssertEqual(t, len(m.submatchesIndex), 0) } func TestMatchAllMultiple(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) matches := r.MatchAll("a 1 b 22 c 333") tst.AssertEqual(t, len(matches), 3) tst.AssertEqual(t, matches[0].FullMatch().Value(), "1") tst.AssertEqual(t, matches[1].FullMatch().Value(), "22") tst.AssertEqual(t, matches[2].FullMatch().Value(), "333") } func TestMatchAllNone(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) matches := r.MatchAll("abc") tst.AssertEqual(t, len(matches), 0) } func TestMatchAllSingle(t *testing.T) { r := W(regexp.MustCompile(`(?P\d+)`)) matches := r.MatchAll("only 42 here") tst.AssertEqual(t, len(matches), 1) tst.AssertEqual(t, matches[0].GroupByName("num").Value(), "42") } func TestReplaceAllNonLiteralExpansion(t *testing.T) { r := W(regexp.MustCompile(`(\w+)@(\w+)`)) out := r.ReplaceAll("hi alice@example, hi bob@example", "$2/$1", false) tst.AssertEqual(t, out, "hi example/alice, hi example/bob") } func TestReplaceAllLiteralNoExpansion(t *testing.T) { r := W(regexp.MustCompile(`(\w+)@(\w+)`)) out := r.ReplaceAll("hi alice@example", "$2/$1", true) tst.AssertEqual(t, out, "hi $2/$1") } func TestReplaceAllFunc(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) out := r.ReplaceAllFunc("a1 b22 c333", func(s string) string { return strings.Repeat("x", len(s)) }) tst.AssertEqual(t, out, "ax bxx cxxx") } func TestRemoveAll(t *testing.T) { r := W(regexp.MustCompile(`\s+`)) out := r.RemoveAll(" hello world ") tst.AssertEqual(t, out, "helloworld") } func TestRemoveAllNoMatch(t *testing.T) { r := W(regexp.MustCompile(`\d+`)) out := r.RemoveAll("no digits here") tst.AssertEqual(t, out, "no digits here") } func TestRemoveAllDoesNotExpandPlaceholders(t *testing.T) { // removal uses literal replacement, so no expansion happens r := W(regexp.MustCompile(`(\w+)`)) out := r.RemoveAll("abc") tst.AssertEqual(t, out, "") } // --- RegexMatch --- func TestRegexMatchFullMatch(t *testing.T) { r := W(regexp.MustCompile(`b\w+d`)) m, ok := r.MatchFirst("aa beard cc") tst.AssertTrue(t, ok) fm := m.FullMatch() tst.AssertEqual(t, fm.Value(), "beard") tst.AssertEqual(t, fm.Start(), 3) tst.AssertEqual(t, fm.End(), 8) } func TestRegexMatchGroupCount(t *testing.T) { r := W(regexp.MustCompile(`(a)(b)(c)`)) m, ok := r.MatchFirst("abc") tst.AssertTrue(t, ok) tst.AssertEqual(t, m.GroupCount(), 3) } func TestRegexMatchGroupByIndex(t *testing.T) { r := W(regexp.MustCompile(`(\w+)-(\w+)-(\w+)`)) m, ok := r.MatchFirst("foo-bar-baz") tst.AssertTrue(t, ok) tst.AssertEqual(t, m.GroupByIndex(0).Value(), "foo-bar-baz") tst.AssertEqual(t, m.GroupByIndex(1).Value(), "foo") tst.AssertEqual(t, m.GroupByIndex(2).Value(), "bar") tst.AssertEqual(t, m.GroupByIndex(3).Value(), "baz") } func TestRegexMatchGroupByName(t *testing.T) { r := W(regexp.MustCompile(`(?P\w+)\s+(?P\w+)`)) m, ok := r.MatchFirst("John Doe") tst.AssertTrue(t, ok) tst.AssertEqual(t, m.GroupByName("first").Value(), "John") tst.AssertEqual(t, m.GroupByName("last").Value(), "Doe") } func TestRegexMatchGroupByNamePanics(t *testing.T) { r := W(regexp.MustCompile(`(?P\d+)`)) m, ok := r.MatchFirst("99") tst.AssertTrue(t, ok) defer func() { rec := recover() tst.AssertTrue(t, rec != nil) }() _ = m.GroupByName("nonexistent") t.Fatal("expected panic") } func TestGroupByNameOrEmptyMissingName(t *testing.T) { r := W(regexp.MustCompile(`(?P\d+)`)) m, ok := r.MatchFirst("99") tst.AssertTrue(t, ok) g := m.GroupByNameOrEmpty("not-present") tst.AssertTrue(t, g.IsEmpty()) tst.AssertFalse(t, g.Exists()) tst.AssertEqual(t, g.ValueOrEmpty(), "") tst.AssertPtrEqual(t, g.ValueOrNil(), nil) } // --- RegexMatchGroup --- func TestRegexMatchGroupAccessors(t *testing.T) { r := W(regexp.MustCompile(`(\w+)`)) m, ok := r.MatchFirst(" hello ") tst.AssertTrue(t, ok) g := m.GroupByIndex(1) tst.AssertEqual(t, g.Value(), "hello") tst.AssertEqual(t, g.Start(), 2) tst.AssertEqual(t, g.End(), 7) s, e := g.Range() tst.AssertEqual(t, s, 2) tst.AssertEqual(t, e, 7) tst.AssertEqual(t, g.Length(), 5) } // --- OptRegexMatchGroup --- func TestOptRegexMatchGroupExisting(t *testing.T) { r := W(regexp.MustCompile(`(?P\w+)`)) m, ok := r.MatchFirst(" hello ") tst.AssertTrue(t, ok) g := m.GroupByNameOrEmpty("word") tst.AssertTrue(t, g.Exists()) tst.AssertFalse(t, g.IsEmpty()) tst.AssertEqual(t, g.Value(), "hello") tst.AssertEqual(t, g.ValueOrEmpty(), "hello") tst.AssertEqual(t, *g.ValueOrNil(), "hello") tst.AssertEqual(t, g.Start(), 2) tst.AssertEqual(t, g.End(), 7) s, e := g.Range() tst.AssertEqual(t, s, 2) tst.AssertEqual(t, e, 7) tst.AssertEqual(t, g.Length(), 5) } func TestOptRegexMatchGroupOptionalNotMatched(t *testing.T) { // group2 is optional and won't match; group1 will r := W(regexp.MustCompile(`(?PA+)(?PB+)?`)) m, ok := r.MatchFirst("AAA") tst.AssertTrue(t, ok) g1 := m.GroupByNameOrEmpty("group1") tst.AssertTrue(t, g1.Exists()) tst.AssertEqual(t, g1.ValueOrEmpty(), "AAA") g2 := m.GroupByNameOrEmpty("group2") tst.AssertTrue(t, g2.IsEmpty()) tst.AssertFalse(t, g2.Exists()) tst.AssertEqual(t, g2.ValueOrEmpty(), "") tst.AssertPtrEqual(t, g2.ValueOrNil(), nil) } // --- Misc combined behavior --- func TestMultipleNamedGroupsAcrossMatches(t *testing.T) { r := W(regexp.MustCompile(`(?P\w+)=(?P\d+)`)) matches := r.MatchAll("a=1 b=22 c=333") tst.AssertEqual(t, len(matches), 3) tst.AssertEqual(t, matches[0].GroupByName("k").Value(), "a") tst.AssertEqual(t, matches[0].GroupByName("v").Value(), "1") tst.AssertEqual(t, matches[1].GroupByName("k").Value(), "b") tst.AssertEqual(t, matches[1].GroupByName("v").Value(), "22") tst.AssertEqual(t, matches[2].GroupByName("k").Value(), "c") tst.AssertEqual(t, matches[2].GroupByName("v").Value(), "333") } func TestEmptyMatchHaystack(t *testing.T) { r := W(regexp.MustCompile(`.*`)) tst.AssertTrue(t, r.IsMatch("")) m, ok := r.MatchFirst("") tst.AssertTrue(t, ok) tst.AssertEqual(t, m.FullMatch().Value(), "") tst.AssertEqual(t, m.FullMatch().Length(), 0) }