// SPDX-License-Identifier: Unlicense package bytes_test import ( "bytes" "errors" "strings" "testing" "git.codemonkeysoftware.net/b/gigaparsec" pbytes "git.codemonkeysoftware.net/b/gigaparsec/bytes" ptest "git.codemonkeysoftware.net/b/gigaparsec/test" pgen "git.codemonkeysoftware.net/b/gigaparsec/test/generator" "github.com/shoenig/test" "github.com/shoenig/test/must" "pgregory.net/rapid" ) func TestRegexp(t *testing.T) { alpha := rapid.SliceOfBytesMatching(`[A-Za-z]{1,100}`) t.Run("position and value are correct after match", rapid.MakeCheck(func(t *rapid.T) { needle := alpha.Draw(t, "needle") input := rapid.Map(alpha, func(suffix []byte) []byte { return append(needle, suffix...) }). Draw(t, "input") p := pbytes.Regexp(string(needle)) result, err := p(gigaparsec.MakeState(bytes.NewReader(input))) succeeded, val, next := result.Status() must.NoError(t, err) test.True(t, succeeded) test.EqOp(t, string(needle), val) ptest.StateIsAt(t, next, uint64(len(needle))) })) t.Run("only searches the beginning of input", rapid.MakeCheck(func(t *rapid.T) { needle := alpha.Draw(t, "needle") input := rapid.Map(alpha, func(prefix []byte) []byte { return append(prefix, needle...) }). Filter(func(b []byte) bool { return !bytes.HasPrefix(b, needle) }). Draw(t, "input") p := pbytes.Regexp(string(needle)) result, err := p(gigaparsec.MakeState(bytes.NewReader(input))) succeeded, _, _ := result.Status() must.NoError(t, err) test.False(t, succeeded) })) t.Run("fails on unexpected error", func(t *testing.T) { expectedErr := errors.New("it broke") p := pbytes.Regexp("nope") result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr))) succeeded, _, _ := result.Status() test.ErrorIs(t, err, expectedErr) test.False(t, succeeded) }) t.Run("returns a useful Got value", func(t *testing.T) { p := pbytes.Regexp("hello") result, err := p(gigaparsec.MakeState(strings.NewReader("hellaparsec"))) must.NoError(t, err) test.StrContains(t, result.Message().Got(), "hella") }) } func TestRuneReader(t *testing.T) { var s = "abcdefghijklmnopqrstuvwxyz" rr := pbytes.NewRuneReader(gigaparsec.MakeState(strings.NewReader(s))) for i, b := range s { r, n, err := rr.ReadRune() test.NoError(t, err) test.EqOp(t, 1, n) test.EqOp(t, r, rune(b)) if t.Failed() { t.Fatalf("failed at %d", i) } } } func TestMatchString(t *testing.T) { t.Run("fails on unexpected error", rapid.MakeCheck(func(t *rapid.T) { s := rapid.StringN(-1, -1, 100).Draw(t, "s") readErr := pgen.Error().Draw(t, "readErr") result, err := pbytes.MatchString(s)(gigaparsec.MakeState(ptest.ErrReaderAt(readErr))) test.ErrorIs(t, err, readErr) success, _, _ := result.Status() test.False(t, success) test.False(t, result.Consumed()) })) t.Run("does not succeed or consume on mismatch", rapid.MakeCheck(func(t *rapid.T) { bgen := rapid.SliceOfN(rapid.Byte(), 1, 100) input := bgen.Draw(t, "input") notPrefix := func(b []byte) bool { return !bytes.HasPrefix(input, b) } s := string(bgen.Filter(notPrefix).Draw(t, "s")) result, err := pbytes.MatchString(s)(gigaparsec.MakeState(bytes.NewReader(input))) test.NoError(t, err) success, _, _ := result.Status() test.False(t, success) test.False(t, result.Consumed()) })) t.Run("succeeds with correct value, consumption, and position", rapid.MakeCheck(func(t *rapid.T) { input := rapid.SliceOfN(rapid.Byte(), 1, 100).Draw(t, "input") slen := rapid.IntRange(0, len(input)).Draw(t, "slen") s := string(input[:slen]) result, err := pbytes.MatchString(s)(gigaparsec.MakeState(bytes.NewReader(input))) must.NoError(t, err) success, value, next := result.Status() must.True(t, success) test.True(t, result.Consumed()) test.EqOp(t, s, value) ptest.StateIsAt(t, next, uint64(slen)) })) }