package gigaparsec_test import ( "bytes" "testing" "git.codemonkeysoftware.net/b/gigaparsec" "git.codemonkeysoftware.net/b/gigaparsec/cursor" ptest "git.codemonkeysoftware.net/b/gigaparsec/test" "git.codemonkeysoftware.net/b/gigaparsec/test/generator" "github.com/shoenig/test" "github.com/shoenig/test/must" "pgregory.net/rapid" ) func Todo(t *testing.T) { t.Errorf("TODO") } func not[T any](pred func(T) bool) func(T) bool { return func(x T) bool { return !pred(x) } } func hasPrefix(prefix []byte) func([]byte) bool { return func(b []byte) bool { return bytes.HasPrefix(b, prefix) } } func TestSlice(t *testing.T) { assertParseFails := func(t rapid.TB, input []byte, p gigaparsec.Parser[byte, []byte]) { t.Helper() start := gigaparsec.MakeState(cursor.NewSlice(input)) result, err := p(start) must.NoError(t, err) failed, consumed, _ := result.Failed() test.True(t, failed) test.False(t, consumed) if t.Failed() { t.FailNow() } } t.Run("fails with wrong contents", rapid.MakeCheck(func(t *rapid.T) { s := rapid.SliceOfN(rapid.Byte(), 1, -1).Draw(t, "s") input := rapid.SliceOfN(rapid.Byte(), len(s), -1). Filter(not(hasPrefix(s))).Draw(t, "input") assertParseFails(t, input, gigaparsec.Slice(s)) })) t.Run("fails at end of input", rapid.MakeCheck(func(t *rapid.T) { s := rapid.SliceOfN(rapid.Byte(), 1, -1).Draw(t, "s") inputLen := rapid.IntRange(0, len(s)-1).Draw(t, "inputLen") input := s[:inputLen] assertParseFails(t, input, gigaparsec.Slice(s)) })) t.Run("fails when read fails", rapid.MakeCheck(func(t *rapid.T) { expectedErr := generator.Error().Draw(t, "expectedErr") c := ptest.ErrCursor[byte](expectedErr) s := rapid.SliceOfN(rapid.Byte(), 0, 100).Draw(t, "s") result, err := gigaparsec.Slice(s)(gigaparsec.MakeState(c)) failed, _, _ := result.Failed() test.ErrorIs(t, err, expectedErr) test.True(t, failed) })) t.Run("succeeds when contents match", rapid.MakeCheck(func(t *rapid.T) { input := rapid.SliceOfN(rapid.Byte(), 1, -1).Draw(t, "input") sLen := rapid.IntRange(0, len(input)).Draw(t, "sLen") s := input[:sLen] start := gigaparsec.MakeState(cursor.NewSlice(input)) result, err := gigaparsec.Slice(s)(start) must.NoError(t, err) succeeded, consumed, value, next, _ := result.Succeeded() test.True(t, succeeded) test.True(t, consumed) test.SliceEqOp(t, s, value) test.EqOp(t, uint64(len(s)), next.Pos()) })) } func TestChoose(t *testing.T) { Todo(t) } func TestBind(t *testing.T) { t.Run("fails without constructing second parser if the first parser fails", Todo) t.Run("returns an error without constructing second parser if the first returns an error", Todo) t.Run("fails if the second parser fails", Todo) t.Run("returns an error if the second parser returns an error", Todo) t.Run("succeeds if both parsers succeed", rapid.MakeCheck(func(t *rapid.T) { // s := rapid.SliceOfN(rapid.Byte(), 0, 100) t.Errorf("TODO") })) t.Run("consumption on success", rapid.MakeCheck(func(t *rapid.T) { makeParser := func(consume bool) gigaparsec.Parser[byte, struct{}] { return func(s gigaparsec.State[byte]) (gigaparsec.Result[byte, struct{}], error) { return gigaparsec.Succeed(consume, struct{}{}, s, gigaparsec.MessageOK(0)), nil } } pConsume := rapid.Bool().Draw(t, "pShouldConsume") qConsume := rapid.Bool().Draw(t, "qShouldConsume") p := makeParser(pConsume) q := func(struct{}) gigaparsec.Parser[byte, struct{}] { return makeParser(qConsume) } result, err := gigaparsec.Bind(p, q)(gigaparsec.MakeState(cursor.NewSlice([]byte{}))) must.NoError(t, err) must.EqOp(t, pConsume || qConsume, result.Consumed()) })) } func TestReturn(t *testing.T) { Todo(t) } func TestMap(t *testing.T) { Todo(t) } func TestSatisfy(t *testing.T) { Todo(t) } func Try(t *testing.T) { Todo(t) } func TestLabel(t *testing.T) { Todo(t) } func TestEnd(t *testing.T) { Todo(t) }