package csexp_test import ( "bytes" "testing" "git.codemonkeysoftware.net/b/peachy-go/csexp" "git.codemonkeysoftware.net/b/peachy-go/csexp/gen" "github.com/shoenig/test" "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) }) } 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) })) 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) })) } 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) })) }