peachy-go/csexp/sexp_test.go

132 lines
3.5 KiB
Go
Raw Normal View History

2024-08-29 05:01:38 +00:00
package csexp_test
import (
"bytes"
"testing"
"git.codemonkeysoftware.net/b/peachy-go/csexp"
"git.codemonkeysoftware.net/b/peachy-go/csexp/gen"
2024-09-18 17:25:19 +00:00
"github.com/shoenig/test"
2024-08-29 05:01:38 +00:00
"github.com/shoenig/test/must"
"pgregory.net/rapid"
)
func TestWriteToAndStringEquivalent(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
sexp := gen.Sexp().Draw(t, "sexp")
stringed := sexp.String()
var buf bytes.Buffer
n, err := sexp.WriteTo(&buf)
must.NoError(t, err)
written := buf.String()
must.EqOp(t, written, stringed)
must.EqOp(t, int64(len(stringed)), n)
})
}
func TestStringAndParseEqual(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
sexp := gen.Sexp().Draw(t, "sexp")
str := sexp.String()
parsed, err := csexp.Parse([]byte(str))
must.NoError(t, err)
must.Equal(t, sexp, parsed)
})
}
2024-09-18 17:25:19 +00:00
func TestParseFails(t *testing.T) {
t.Run("with extra input", rapid.MakeCheck(func(t *rapid.T) {
sexp := gen.Sexp().Draw(t, "sexp")
b := []byte(sexp.String())
extra := rapid.SliceOfN(rapid.Byte(), 1, -1).Draw(t, "extra")
b = append(b, extra...)
parsed, err := csexp.Parse(b)
test.Error(t, err)
test.Nil(t, parsed)
}))
2024-09-18 17:46:39 +00:00
t.Run("with too little input", rapid.MakeCheck(func(t *rapid.T) {
sexp := gen.Sexp().Draw(t, "sexp")
b := []byte(sexp.String())
missing := rapid.IntRange(1, len(b)).Draw(t, "missing")
b = b[:len(b)-missing]
parsed, err := csexp.Parse(b)
test.Error(t, err)
test.Nil(t, parsed)
}))
t.Run("with malformed atom length", rapid.MakeCheck(func(t *rapid.T) {
atom := gen.Atom().Draw(t, "atom")
s := []byte(atom.String())
changeIdx := rapid.IntRange(0, bytes.IndexByte(s, ':')).Draw(t, "changeIdx")
change := rapid.ByteRange(1, 255).Draw(t, "change")
s[changeIdx] = change
parsed, err := csexp.Parse(s)
test.Error(t, err)
test.Nil(t, parsed)
}))
2024-09-18 17:25:19 +00:00
}
2024-08-29 05:01:38 +00:00
func TestCloneEqual(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
sexp := gen.Sexp().Draw(t, "sexp")
cloned := sexp.Clone()
must.Equal(t, sexp, cloned)
})
}
func MustNotBeEqual(t rapid.TB, s1, s2 csexp.Sexp) {
e1 := s1.Equal(s2)
e2 := s2.Equal(s1)
if e1 == !e2 {
t.Logf("expected Sexp.Equal to be commutative, but got different results")
}
if e1 || e2 {
t.Fatalf("expected sexps not to be equal\ns1: %q\ns2: %q", string(s1.String()), string(s2.String()))
}
}
func TestNotEqual(t *testing.T) {
t.Run("append to atom", rapid.MakeCheck(func(t *rapid.T) {
a1 := gen.Atom().Draw(t, "a1")
a2 := a1.Clone().(csexp.Atom)
a2 = append(a2, 'x')
MustNotBeEqual(t, a1, a2)
}))
t.Run("change atom byte", rapid.MakeCheck(func(t *rapid.T) {
nonEmpty := func(a csexp.Atom) bool { return len(a) >= 1 }
a1 := gen.Atom().Filter(nonEmpty).Draw(t, "a1")
a2 := a1.Clone().(csexp.Atom)
a2[0] = a2[0] + 1
MustNotBeEqual(t, a1, a2)
}))
t.Run("append to list", rapid.MakeCheck(func(t *rapid.T) {
l1 := gen.List().Draw(t, "l1")
extraElement := gen.Sexp().Draw(t, "extraElement")
l2 := l1.Clone().(csexp.List)
l2 = append(l2, extraElement)
MustNotBeEqual(t, l1, l2)
}))
t.Run("change list element", rapid.MakeCheck(func(t *rapid.T) {
nonEmpty := func(l csexp.List) bool { return len(l) >= 1 }
l1 := gen.List().Filter(nonEmpty).Draw(t, "l1")
isDifferent := func(s csexp.Sexp) bool { return !s.Equal(l1[0]) }
newElement := gen.Sexp().Filter(isDifferent).Draw(t, "newElement")
l2 := l1.Clone().(csexp.List)
l2[0] = newElement
MustNotBeEqual(t, l1, l2)
}))
t.Run("list and atom", rapid.MakeCheck(func(t *rapid.T) {
atom := gen.Atom().Draw(t, "atom")
list := gen.List().Draw(t, "list")
MustNotBeEqual(t, atom, list)
}))
}