miller/pkg/lib/regex_test.go
2023-12-17 12:46:28 -05:00

190 lines
5.6 KiB
Go

// ================================================================
// Most Miller tests (thousands of them) are command-line-driven via
// mlr regtest. Here are some cases needing special focus.
// ================================================================
package lib
import (
"testing"
)
// ----------------------------------------------------------------
type tDataForHasCaptures struct {
replacement string
expectedHasCaptures bool
expectedMatrix [][]int
}
type tDataForSubGsub struct {
input string
sregex string
replacement string
expectedOutput string
}
type tDataForMatches struct {
input string
sregex string
expectedOutput bool
expectedCaptures []string
}
// ----------------------------------------------------------------
var dataForHasCaptures = []tDataForHasCaptures{
{"foo", false, nil},
{"\\0", true, [][]int{{0, 2, 0, 2}}},
{"\\3", true, [][]int{{0, 2, 0, 2}}},
{"\\34", true, [][]int{{0, 2, 0, 2}}},
{"abc\\1def\\2ghi", true, [][]int{{3, 5, 3, 5}, {8, 10, 8, 10}}},
}
var dataForSub = []tDataForSubGsub{
{"abcde", "c", "X", "abXde"},
{"abcde", "z", "X", "abcde"},
{"abcde", "[a-z]", "X", "Xbcde"},
{"abcde", "[A-Z]", "X", "abcde"},
{"abcde", "c", "X", "abXde"},
{"abcde", "z", "X", "abcde"},
{"abcde", "[a-z]", "X", "Xbcde"},
{"abcde", "[A-Z]", "X", "abcde"},
{"ab_cde", "(..)_(...)", "\\2\\1", "cdeab"},
{"ab_cde", "(..)_(...)", "\\2-\\1", "cde-ab"},
{"ab_cde", "(..)_(...)", "X\\2Y\\1Z", "XcdeYabZ"},
{"foofoofoo", "(f.o)", "b\\1r", "bfoorfoofoo"},
{"foofoofoo", "(f.*o)", "b\\1r", "bfoofoofoor"},
{"foofoofoo", "(f.o)", "b\\2r", "brfoofoo"},
{"foofoofoo", "(f.*o)", "b\\2r", "br"},
}
var dataForGsub = []tDataForSubGsub{
{"abcde", "c", "X", "abXde"},
{"abcde", "z", "X", "abcde"},
{"abcde", "[a-z]", "X", "XXXXX"},
{"abcde", "[A-Z]", "X", "abcde"},
{"abcde", "[c-d]", "X", "abXXe"},
{"abcde", "c", "X", "abXde"},
{"abcde", "z", "X", "abcde"},
{"abcde", "[a-z]", "X", "XXXXX"},
{"abcde", "[A-Z]", "X", "abcde"},
{"abcde", "[c-d]", "X", "abXXe"},
{"abacad", "a(.)", "<\\1>", "<b><c><d>"},
{"abacad", "a(.)", "<\\2>", "<><><>"},
}
var dataForMatches = []tDataForMatches{
{"abcde", "[A-Z]", false, []string{"", "", "", "", "", "", "", "", "", ""}},
{"abcde", "[a-z]", true, []string{"a", "", "", "", "", "", "", "", "", ""}},
{"...ab_cde...", "(..)_(...)", true, []string{"ab_cde", "ab", "cde", "", "", "", "", "", "", ""}},
{"...ab_cde...fg_hij...", "(..)_(...)", true, []string{"ab_cde", "ab", "cde", "", "", "", "", "", "", ""}},
{"foofoofoo", "(f.o)", true, []string{"foo", "foo", "", "", "", "", "", "", "", ""}},
{"foofoofoo", "(f.*o)", true, []string{"foofoofoo", "foofoofoo", "", "", "", "", "", "", "", ""}},
}
func TestRegexReplacementHasCaptures(t *testing.T) {
for i, entry := range dataForHasCaptures {
actualHasCaptures, actualMatrix := ReplacementHasCaptures(entry.replacement)
if actualHasCaptures != entry.expectedHasCaptures {
t.Fatalf("case %d replacement \"%s\" expected %v got %v\n",
i, entry.replacement, entry.expectedHasCaptures, actualHasCaptures,
)
}
if !compareMatrices(actualMatrix, entry.expectedMatrix) {
t.Fatalf("case %d replacement \"%s\" expected matrix %#v got %#v\n",
i, entry.replacement, entry.expectedMatrix, actualMatrix,
)
}
}
}
func TestRegexSub(t *testing.T) {
for i, entry := range dataForSub {
actualOutput := RegexStringSub(entry.input, entry.sregex, entry.replacement)
if actualOutput != entry.expectedOutput {
t.Fatalf("case %d input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
i, entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
)
}
}
}
func TestRegexGsub(t *testing.T) {
for i, entry := range dataForGsub {
actualOutput := RegexStringGsub(entry.input, entry.sregex, entry.replacement)
if actualOutput != entry.expectedOutput {
t.Fatalf("case %d input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
i, entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
)
}
}
}
func TestRegexMatches(t *testing.T) {
for i, entry := range dataForMatches {
actualOutput, actualCaptures := RegexStringMatchWithCaptures(entry.input, entry.sregex)
if actualOutput != entry.expectedOutput {
t.Fatalf("case %d input \"%s\" sregex \"%s\" expected %v got %v\n",
i, entry.input, entry.sregex, entry.expectedOutput, actualOutput,
)
}
if !compareCaptures(actualCaptures, entry.expectedCaptures) {
t.Fatalf("case %d input \"%s\" sregex \"%s\" expected captures %#v got %#v\n",
i, entry.input, entry.sregex, entry.expectedCaptures, actualCaptures,
)
}
}
}
func compareMatrices(
actualMatrix [][]int,
expectedMatrix [][]int,
) bool {
if actualMatrix == nil && expectedMatrix == nil {
return true
}
if actualMatrix == nil || expectedMatrix == nil {
return false
}
if len(actualMatrix) != len(expectedMatrix) {
return false
}
for i := range expectedMatrix {
actualRow := actualMatrix[i]
expectedRow := expectedMatrix[i]
if len(actualRow) != len(expectedRow) {
return false
}
for j := range expectedRow {
if actualRow[j] != expectedRow[j] {
return false
}
}
}
return true
}
func compareCaptures(
actualCaptures []string,
expectedCaptures []string,
) bool {
if actualCaptures == nil && expectedCaptures == nil {
return true
}
if actualCaptures == nil || expectedCaptures == nil {
return false
}
if len(actualCaptures) != len(expectedCaptures) {
return false
}
for i := range expectedCaptures {
if actualCaptures[i] != expectedCaptures[i] {
return false
}
}
return true
}