Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify that protocols and records are not "empty" #141

Merged
merged 1 commit into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions tooling/pkg/dsl/validation_computed_fields_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
func TestDuplicateComputedFieldName(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
f: 1
f: 2`
Expand All @@ -22,6 +24,8 @@ X: !record
func TestUnboundField(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
f: missingField`
_, err := parseAndValidate(t, src)
Expand All @@ -31,6 +35,8 @@ X: !record
func TestRecursiveField(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
f: f`
_, err := parseAndValidate(t, src)
Expand All @@ -40,6 +46,8 @@ X: !record
func TestCycle(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
a: b
b: c
Expand All @@ -51,6 +59,8 @@ X: !record
func TestNoCycle(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
a: b
b: c
Expand All @@ -62,6 +72,8 @@ X: !record
func TestIntTooLarge(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
a: 0xFFFFFFFFFFFFFFFF1`
_, err := parseAndValidate(t, src)
Expand All @@ -71,6 +83,8 @@ X: !record
func TestNegativeIntTooLarge(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
a: -0x8000000000000001`
_, err := parseAndValidate(t, src)
Expand All @@ -94,6 +108,8 @@ Y: !record
func TestChainedExpressionTargetNotResolved(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
yInt: y.anInt`
_, err := parseAndValidate(t, src)
Expand Down Expand Up @@ -187,6 +203,8 @@ X: !record
func TestIndexOnUnresolved(t *testing.T) {
src := `
R: !record
fields:
unused: int
X: !record
fields:
r: R
Expand Down Expand Up @@ -398,6 +416,7 @@ func TestSizeNoArgs(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
vSize: size()`
_, err := parseAndValidate(t, src)
Expand All @@ -408,6 +427,7 @@ func TestSizeSingleArgWrongType(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
vSize: size(1)`
_, err := parseAndValidate(t, src)
Expand Down Expand Up @@ -552,6 +572,8 @@ X: !record
func TestArrayDimensionIndexInvalidArg1Type(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
dimIndeX: dimensionIndex(1, "x")`
_, err := parseAndValidate(t, src)
Expand Down Expand Up @@ -634,6 +656,8 @@ X: !record
func TestArrayDimensionCountInvalidArgType(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
dimCount: dimensionCount(1)`
_, err := parseAndValidate(t, src)
Expand All @@ -643,6 +667,8 @@ X: !record
func TestSwitchErrorInTarget(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
c:
!switch missing:
Expand Down Expand Up @@ -822,6 +848,8 @@ X: !record
func TestArithmeticIncompatibleOperands(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
c1: 1 + "2"
`
Expand All @@ -845,6 +873,8 @@ X: !record
func TestCastToUnrecognizedType(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
c1: 1 as what
`
Expand All @@ -854,6 +884,8 @@ X: !record
func TestValidCasts(t *testing.T) {
src := `
X: !record
fields:
unused: int
computedFields:
c1: 1 as int
c2: 1.1 as int
Expand Down
2 changes: 2 additions & 0 deletions tooling/pkg/dsl/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Rec: !record
func TestRecordComputedFieldNameInvalid(t *testing.T) {
src := `
Rec: !record
fields:
unused: int
computedFields:
A: 1
`
Expand Down
2 changes: 2 additions & 0 deletions tooling/pkg/dsl/validation_topological_sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ R1: !record
fields:
f1: R2<R1>
R2<T>: !record
fields:
f1: T
`
_, err := parseAndValidate(t, src)
require.ErrorContains(t, err, "there is a reference cycle, which is not supported, within namespace 'test': Record 'R1' -> Field 'f1' -> Record 'R1'")
Expand Down
6 changes: 6 additions & 0 deletions tooling/pkg/dsl/validation_type_resolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
func TestTypeNamesNotUnique(t *testing.T) {
src := `
X: !record
fields:
unused: int
X: !enum
`
_, err := parseAndValidate(t, src)
Expand All @@ -21,7 +23,11 @@ X: !enum
func TestTypeNamesNotUniqueOneWithGenericParameters(t *testing.T) {
src := `
X<T>: !record
fields:
unused: T
X<T1, T2>: !record
fields:
unused: [T1, T2]
`
_, err := parseAndValidate(t, src)
assert.ErrorContains(t, err, "the name 'X' is already defined in ")
Expand Down
5 changes: 4 additions & 1 deletion tooling/pkg/dsl/validation_unions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ X: !record
f: !union
g1: GenericRecord<int>
g2: GenericRecord<int>
GenericRecord<T>: !record`
GenericRecord<T>: !record
fields:
unused: T
`

_, err := parseAndValidate(t, src)
assert.ErrorContains(t, err, "redundant union type cases")
Expand Down
19 changes: 19 additions & 0 deletions tooling/pkg/dsl/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func (meta *DefinitionMeta) UnmarshalYAML(value *yaml.Node) error {
}

func (rec *RecordDefinition) UnmarshalYAML(value *yaml.Node) error {
parsedFields := false
for i := 0; i < len(value.Content); i += 2 {
k := value.Content[i]
v := value.Content[i+1]
Expand All @@ -160,16 +161,26 @@ func (rec *RecordDefinition) UnmarshalYAML(value *yaml.Node) error {
if err != nil {
return err
}
if len(rec.Fields) > 0 {
parsedFields = true
}
case "computedFields":
err := v.DecodeWithOptions(&rec.ComputedFields, yaml.DecodeOptions{KnownFields: true})
if err != nil {
return err
}
if len(rec.ComputedFields) == 0 {
return parseError(value, "!record specification computedFields cannot be empty")
}
default:
return parseError(k, "field '%s' is not valid on a !record specification", k.Value)
}
}

if !parsedFields {
return parseError(value, "!record specification must define at least one field")
}

return nil
}

Expand Down Expand Up @@ -440,6 +451,7 @@ func convertPattern(pat *parser.Pattern, node NodeMeta) Pattern {
}

func (protocol *ProtocolDefinition) UnmarshalYAML(value *yaml.Node) error {
parsedSequence := false
for i := 0; i < len(value.Content); i += 2 {
k := value.Content[i]
v := value.Content[i+1]
Expand All @@ -449,11 +461,18 @@ func (protocol *ProtocolDefinition) UnmarshalYAML(value *yaml.Node) error {
if err != nil {
return err
}
if len(protocol.Sequence) > 0 {
parsedSequence = true
}
default:
return parseError(k, "field '%s' is not valid on a !protocol specification", k.Value)
}
}

if !parsedSequence {
return parseError(value, "!protocol specification must define a non-empty sequence")
}

return nil
}

Expand Down
25 changes: 25 additions & 0 deletions tooling/pkg/dsl/yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@ func TestBasicErrors(t *testing.T) {
}
}

func TestEmptyFieldErrors(t *testing.T) {
testCases := []struct {
src string
err string
}{
{"P: !protocol", "must define a non-empty sequence"},
{"P: !protocol\n sequence:", "must define a non-empty sequence"},
{"R: !record", "must define at least one field"},
{"R: !record\n fields:", "must define at least one field"},
{`
R: !record
fields:
x: int
computedFields:`, "computedFields cannot be empty"},
}

for _, tC := range testCases {
t.Run(tC.src, func(t *testing.T) {
_, err := parse(t, tC.src)
require.ErrorContains(t, err, tC.err)
})
}

}

func TestCommentsOnRecords(t *testing.T) {
src := `
# This is a comment on a record
Expand Down
Loading