Files
dolt/nomdl/pkg/parse_test.go
Erik Arvidsson cdcf952270 Update TypeRef comment and get rid of useless param
The name param of MakeCompoundTypeRef is always the empty string.

I didn't change the underlying storage or serialization.

Fixes #436, #477
2015-10-26 15:35:45 -04:00

478 lines
14 KiB
Go

package pkg
import (
"fmt"
"strings"
"testing"
"github.com/attic-labs/noms/Godeps/_workspace/src/github.com/stretchr/testify/suite"
"github.com/attic-labs/noms/d"
"github.com/attic-labs/noms/ref"
"github.com/attic-labs/noms/types"
)
const (
union = "union { bool :Bool t2 :Blob }"
structTmpl = "struct %s { %s %s }"
)
func TestParserTestSuite(t *testing.T) {
suite.Run(t, &ParserTestSuite{})
suite.Run(t, &ParsedResultTestSuite{})
}
type ParserTestSuite struct {
suite.Suite
}
func (suite *ParserTestSuite) parsePanics(test, msg string) {
suite.Panics(func() { runParser("", strings.NewReader(test)) }, msg)
}
func (suite *ParserTestSuite) parseNotPanics(test string) {
suite.NotPanics(func() { runParser("", strings.NewReader(test)) }, test)
}
func (suite *ParserTestSuite) TestAlias() {
importTmpl := `alias Noms = import "%s"`
ref := "sha1-ffffffff"
path := `some/path/\"quotes\"/path`
pkg := runParser("", strings.NewReader(fmt.Sprintf(importTmpl, ref)))
suite.Equal(ref, pkg.Aliases["Noms"])
pkg = runParser("", strings.NewReader(fmt.Sprintf(importTmpl, path)))
suite.Equal(path, pkg.Aliases["Noms"])
}
func (suite *ParserTestSuite) TestUsing() {
usingDecls := `
using Map(String, Simple)
using List(Noms.Commit)
`
pkg := runParser("", strings.NewReader(usingDecls))
suite.Len(pkg.UsingDeclarations, 2)
suite.Equal(types.MapKind, pkg.UsingDeclarations[0].Desc.Kind())
suite.True(types.MakePrimitiveTypeRef(types.StringKind).Equals(pkg.UsingDeclarations[0].Desc.(types.CompoundDesc).ElemTypes[0]))
suite.True(types.MakeUnresolvedTypeRef("", "Simple").Equals(pkg.UsingDeclarations[0].Desc.(types.CompoundDesc).ElemTypes[1]))
suite.Equal(types.ListKind, pkg.UsingDeclarations[1].Desc.Kind())
elemTypes := pkg.UsingDeclarations[1].Desc.(types.CompoundDesc).ElemTypes
suite.Len(elemTypes, 1)
suite.True(types.MakeUnresolvedTypeRef("Noms", "Commit").Equals(elemTypes[0]))
}
func (suite *ParserTestSuite) TestBadUsing() {
suite.Panics(func() { runParser("", strings.NewReader("using Blob")) }, "Can't 'use' a primitive.")
suite.Panics(func() { runParser("", strings.NewReader("using Noms.Commit")) }, "Can't 'use' a type from another package.")
suite.Panics(func() { runParser("", strings.NewReader("using f@(k")) }, "Can't 'use' illegal identifier.")
}
func (suite *ParserTestSuite) TestBadStructParse() {
noFields := "struct str { }"
suite.parsePanics(noFields, "Struct must have fields.")
noName := "struct { a :Bool }"
suite.parsePanics(noName, "Struct must have name.")
badName := "struct *ff { a :Bool }"
suite.parsePanics(badName, "Struct must have legal name.")
dupName := "struct str { a :Bool a :Bool }"
suite.parsePanics(dupName, "Fields must have unique names.")
dupNameInUnion := "struct s { union { a: Bool a :Int32 } }"
suite.parsePanics(dupNameInUnion, "union choices must have unique names.")
dupNameInNamedUnion := "struct s { u :union { a: Bool a :Int32 } }"
suite.parsePanics(dupNameInNamedUnion, "union choices must have unique names.")
twoAnonUnion := fmt.Sprintf(structTmpl, "str", union, union)
suite.parsePanics(twoAnonUnion, "Can't have two anonymous unions.")
optionalAsTypeName := "struct S { x: optional }"
suite.parsePanics(optionalAsTypeName, "optional requires a type after it")
optionalAsTypeName2 := "struct S { x: optional y: T }"
suite.parsePanics(optionalAsTypeName2, "optional requires a type after it")
}
func (suite *ParserTestSuite) TestStructParse() {
oneLine := "struct str { a :Bool b : Blob c: Blob }"
suite.parseNotPanics(oneLine)
noSpace := "struct str{a:Bool}"
suite.parseNotPanics(noSpace)
multiLine := "\nstruct str {\na :Bool\n}"
suite.parseNotPanics(multiLine)
anonUnion := fmt.Sprintf(structTmpl, "str", "a :Bool\n", union)
suite.parseNotPanics(anonUnion)
namedUnions := fmt.Sprintf(structTmpl, "str", "a :Bool\nun1 :"+union, "un2 :"+union)
suite.parseNotPanics(namedUnions)
for k, v := range types.KindToString {
if types.IsPrimitiveKind(k) {
suite.parseNotPanics(fmt.Sprintf(structTmpl, "s", "a :"+v, ""))
suite.parseNotPanics(fmt.Sprintf(structTmpl, "s", "a: optional "+v, ""))
}
}
optional := "struct str { a: optional Bool b: optional Blob c: optional Blob }"
suite.parseNotPanics(optional)
optionalNoSpace := "struct str{a:optional Bool}"
suite.parseNotPanics(optionalNoSpace)
}
func (suite *ParserTestSuite) TestComment() {
comments := []string{
"/* Yo\n*/struct str { a :Bool }",
"struct str { a :Bool }\n/* Yo*/",
"/* Yo\n * is my name */\nstruct str { a :Bool }",
"/* Yo *//* is my name */struct str { a :Bool }",
"struct /*Yo*/ s { a :Bool }",
"struct s /*Yo*/ { a :Bool }",
"struct s { /*Yo*/ a :Bool }",
"struct s { a /*Yo*/ :Bool }",
"struct s { a :/*Yo*/ Bool }",
"struct s { a :Bool/*Yo*/}",
"// Yo\nstruct str { a :Bool }",
"struct str { a :Bool }\n// Yo",
"\n // Yo \t\nstruct str { a :Bool }\n /*More Yo*/",
`// Yo //
// Yo Again
struct str { a :Bool }`,
`struct /* // up in here */s {
a :Bool//Field a
}`,
`struct s {
a :Bool //Field a
// Not a field
}
/* More talk */
struct t { b :Bool }
struct t2 { b:optional/* x */Bool }
struct t3 { b:/* x */optional Bool }`,
}
for _, c := range comments {
suite.parseNotPanics(c)
}
}
func (suite *ParserTestSuite) TestBadComment() {
comments := []string{
"st/* Yo */ruct str { a :Bool }",
"struct str { a :Bool }\n* Yo*/",
"/* Yo *\nstruct str { a :Bool }",
"struct str // OOps { a :Bool }",
"struct str { a :Bool }\n/ Yo",
}
for _, c := range comments {
suite.parsePanics(c, c)
}
}
func (suite *ParserTestSuite) TestEnum() {
enumTmpl := `enum %s { %s }`
name := "Enum"
ids := []string{"e1", "e2", "e4"}
enum := fmt.Sprintf(enumTmpl, name, strings.Join(ids, "\n"))
pkg := runParser("", strings.NewReader(enum))
t := pkg.Types[0]
suite.Equal(name, t.Name())
suite.EqualValues(ids, t.Desc.(types.EnumDesc).IDs)
}
type ParsedResultTestSuite struct {
suite.Suite
primField testField
primOptionalField testField
compoundField testField
compoundOfCompoundField testField
mapOfNamedTypeField testField
namedTypeField testField
namespacedTypeField testField
union types.Choices
}
func (suite *ParsedResultTestSuite) SetupTest() {
suite.primField = testField{"a", types.MakePrimitiveTypeRef(types.Int64Kind), false}
suite.primOptionalField = testField{"b", types.MakePrimitiveTypeRef(types.Float64Kind), true}
suite.compoundField = testField{"set", types.MakeCompoundTypeRef(types.SetKind, types.MakePrimitiveTypeRef(types.StringKind)), false}
suite.compoundOfCompoundField = testField{
"listOfSet",
types.MakeCompoundTypeRef(types.ListKind,
types.MakeCompoundTypeRef(types.SetKind, types.MakePrimitiveTypeRef(types.StringKind))), false}
suite.mapOfNamedTypeField = testField{
"mapOfStructToOther",
types.MakeCompoundTypeRef(types.MapKind,
types.MakeUnresolvedTypeRef("", "Struct"),
types.MakeUnresolvedTypeRef("Elsewhere", "Other"),
),
false}
suite.namedTypeField = testField{"otherStruct", types.MakeUnresolvedTypeRef("", "Other"), false}
suite.namespacedTypeField = testField{"namespacedStruct", types.MakeUnresolvedTypeRef("Elsewhere", "Other"), false}
suite.union = types.Choices{
types.Field{"a", types.MakePrimitiveTypeRef(types.Int32Kind), false},
types.Field{"n", types.MakeUnresolvedTypeRef("NN", "Other"), false},
types.Field{"c", types.MakePrimitiveTypeRef(types.UInt32Kind), false},
}
}
type structTestCase struct {
Name string
Union types.Choices
Fields []testField
}
func makeStructTestCase(n string, u types.Choices, fields ...testField) structTestCase {
return structTestCase{n, u, fields}
}
func (s structTestCase) toText() string {
return fmt.Sprintf(structTmpl, s.Name, s.fieldsToString(), s.unionToString())
}
func (s structTestCase) fieldsToString() (out string) {
for _, f := range s.Fields {
out += f.Name + " :"
if f.Optional {
out += "optional "
}
out += f.D.Describe() + "\n"
}
return
}
func (s structTestCase) unionToString() string {
if s.Union == nil {
return ""
}
return s.Union.Describe()
}
type testField struct {
Name string
D describable
Optional bool
}
func (t testField) toField() types.Field {
return types.Field{t.Name, t.D.(types.TypeRef), t.Optional}
}
type describable interface {
Describe() string
}
func (suite *ParsedResultTestSuite) findTypeByName(n string, ts []types.TypeRef) types.TypeRef {
for _, t := range ts {
if n == t.Name() {
return t
}
}
suite.Fail("Failed to find type by name")
return types.TypeRef{}
}
func (suite *ParsedResultTestSuite) checkStruct(pkg intermediate, s structTestCase) {
typ := suite.findTypeByName(s.Name, pkg.Types)
typFields := typ.Desc.(types.StructDesc).Fields
typUnion := typ.Desc.(types.StructDesc).Union
suite.Equal(s.Name, typ.Name())
suite.Len(typFields, len(s.Fields))
for i, f := range s.Fields {
// Named unions are syntactic sugar for a struct Field that points to an anonymous struct containing an anonymous union.
// So, if the field in the test input was a union...
if _, ok := f.D.(types.Choices); ok {
// ...make sure the names are the same...
suite.Equal(f.Name, typFields[i].Name)
suite.Equal(f.Optional, typFields[i].Optional)
// and the TypeRef points to somewhere else.
suite.True(typFields[i].T.IsUnresolved())
suite.True(typFields[i].T.Ordinal() > 0)
suite.Equal(ref.Ref{}, typFields[i].T.PackageRef())
} else {
suite.EqualValues(s.Fields[i].toField(), typFields[i])
}
}
if s.Union != nil && suite.NotEmpty(typUnion) {
suite.Len(typUnion, len(s.Union))
for i := range s.Union {
suite.EqualValues(s.Union[i], typUnion[i])
}
} else {
suite.EqualValues(s.Union, typUnion, "If s.Union is nil, so should typUnion be.")
}
}
func (suite *ParsedResultTestSuite) parseAndCheckStructs(structs ...structTestCase) {
pkgDef := ""
for _, s := range structs {
pkgDef += s.toText() + "\n"
}
err := d.Try(func() {
pkg := runParser("", strings.NewReader(pkgDef))
for _, s := range structs {
suite.checkStruct(pkg, s)
}
})
suite.NoError(err, pkgDef)
}
func (suite *ParsedResultTestSuite) TestPrimitiveField() {
suite.parseAndCheckStructs(makeStructTestCase("Simple", nil, suite.primField))
}
func (suite *ParsedResultTestSuite) TestPrimitiveOptionalField() {
suite.parseAndCheckStructs(makeStructTestCase("SimpleOptional", nil, suite.primOptionalField))
}
func (suite *ParsedResultTestSuite) TestAnonUnion() {
suite.parseAndCheckStructs(makeStructTestCase("WithAnon", suite.union, suite.primField))
}
func (suite *ParsedResultTestSuite) TestAnonUnionFirst() {
anonUnionFirst := makeStructTestCase("WithAnonFirst", suite.union, suite.primField)
pkgDef := fmt.Sprintf(structTmpl, anonUnionFirst.Name, anonUnionFirst.unionToString(), anonUnionFirst.fieldsToString())
err := d.Try(func() {
pkg := runParser("", strings.NewReader(pkgDef))
suite.checkStruct(pkg, anonUnionFirst)
})
suite.NoError(err, pkgDef)
}
func (suite *ParsedResultTestSuite) TestCommentNextToName() {
withComment := makeStructTestCase("WithComment", suite.union, suite.primField)
pkgDef := fmt.Sprintf(structTmpl, "/* Oy! */"+withComment.Name, withComment.unionToString(), withComment.fieldsToString())
err := d.Try(func() {
pkg := runParser("", strings.NewReader(pkgDef))
suite.checkStruct(pkg, withComment)
})
suite.NoError(err, pkgDef)
}
func (suite *ParsedResultTestSuite) TestCommentAmongFields() {
withComment := makeStructTestCase("WithComment", suite.union, suite.primField)
pkgDef := fmt.Sprintf(structTmpl, withComment.Name, withComment.fieldsToString()+"\n// Nope\n", withComment.unionToString())
err := d.Try(func() {
pkg := runParser("", strings.NewReader(pkgDef))
suite.checkStruct(pkg, withComment)
})
suite.NoError(err, pkgDef)
}
func (suite *ParsedResultTestSuite) TestCompoundField() {
suite.parseAndCheckStructs(makeStructTestCase("Compound", suite.union, suite.compoundField))
}
func (suite *ParsedResultTestSuite) TestCompoundOfCompoundField() {
suite.parseAndCheckStructs(makeStructTestCase("CofC", suite.union, suite.compoundOfCompoundField))
}
func (suite *ParsedResultTestSuite) TestNamedTypeField() {
suite.parseAndCheckStructs(makeStructTestCase("Named", suite.union, suite.namedTypeField))
}
func (suite *ParsedResultTestSuite) TestNamespacedTypeField() {
suite.parseAndCheckStructs(makeStructTestCase("Namespaced", suite.union, suite.namespacedTypeField))
}
func (suite *ParsedResultTestSuite) TestMapOfNamedTypeField() {
suite.parseAndCheckStructs(makeStructTestCase("MapStruct", suite.union, suite.mapOfNamedTypeField))
}
func (suite *ParsedResultTestSuite) TestMultipleFields() {
suite.parseAndCheckStructs(makeStructTestCase("Multi", suite.union,
suite.primField,
suite.primOptionalField,
suite.namedTypeField,
suite.namespacedTypeField,
suite.compoundField,
suite.compoundOfCompoundField,
testField{"namedUnion", suite.union, false},
))
}
func (suite *ParsedResultTestSuite) TestNamedAndAnonUnion() {
suite.parseAndCheckStructs(makeStructTestCase("NamedAndAnon", suite.union,
testField{"namedUnion", suite.union, false},
))
}
func (suite *ParsedResultTestSuite) TestNamedUnionOnly() {
suite.parseAndCheckStructs(makeStructTestCase("NamedUnionOnly", nil,
testField{"namedUnion", suite.union, false},
))
}
func (suite *ParsedResultTestSuite) TestTwoNamedAndAnonUnion() {
suite.parseAndCheckStructs(makeStructTestCase("TwoNamedAndAnon", suite.union,
testField{"namedUnion1", suite.union, false},
testField{"namedUnion2", suite.union, false},
))
}
func (suite *ParsedResultTestSuite) TestMultipleStructs() {
defns := []structTestCase{
makeStructTestCase("Simple", nil, suite.primField),
makeStructTestCase("Optional", nil, suite.primOptionalField),
makeStructTestCase("Compound", nil, suite.compoundField),
makeStructTestCase("CompoundWithUnion", suite.union, suite.compoundField),
makeStructTestCase("TwoNamedAndAnon", suite.union,
testField{"namedUnion1", suite.union, false},
testField{"namedUnion2", suite.union, false},
),
makeStructTestCase("Multi", suite.union,
suite.primField,
suite.primOptionalField,
suite.namespacedTypeField,
suite.compoundField,
testField{"namedUnion", suite.union, false},
),
}
suite.parseAndCheckStructs(defns...)
}
func (suite *ParsedResultTestSuite) TestExpandStruct() {
code := `
struct T {
x: Int32
u: union {
s: String
b: Bool
}
}
`
pkg := runParser("", strings.NewReader(code))
suite.Len(pkg.Types, 2)
{
code := `
struct T {
a: union {
b: String
c: Bool
}
d: union {
e: String
f: Bool
}
}
`
pkg := runParser("", strings.NewReader(code))
suite.Len(pkg.Types, 3)
}
}