5477fecfad
Previously we tolerated EOF as an alias for newline, but a file without a newline at the end is a edge case primarily limited to contrived examples in unit tests, and requiring it simplifies tasks such as code generation in zclwrite, since we can always assume that every block item comes with a built-in line terminator.
252 lines
4.1 KiB
Go
252 lines
4.1 KiB
Go
package zcldec
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-zcl/zcl"
|
|
"github.com/zclconf/go-zcl/zcl/zclsyntax"
|
|
)
|
|
|
|
func TestDecode(t *testing.T) {
|
|
tests := []struct {
|
|
config string
|
|
spec Spec
|
|
ctx *zcl.EvalContext
|
|
want cty.Value
|
|
diagCount int
|
|
}{
|
|
{
|
|
``,
|
|
&ObjectSpec{},
|
|
nil,
|
|
cty.EmptyObjectVal,
|
|
0,
|
|
},
|
|
{
|
|
"a = 1\n",
|
|
&ObjectSpec{},
|
|
nil,
|
|
cty.EmptyObjectVal,
|
|
1, // attribute named "a" is not expected here
|
|
},
|
|
{
|
|
"a = 1\n",
|
|
&ObjectSpec{
|
|
"a": &AttrSpec{
|
|
Name: "a",
|
|
Type: cty.Number,
|
|
},
|
|
},
|
|
nil,
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"a": cty.NumberIntVal(1),
|
|
}),
|
|
0,
|
|
},
|
|
{
|
|
"a = 1\n",
|
|
&AttrSpec{
|
|
Name: "a",
|
|
Type: cty.Number,
|
|
},
|
|
nil,
|
|
cty.NumberIntVal(1),
|
|
0,
|
|
},
|
|
{
|
|
"a = \"1\"\n",
|
|
&AttrSpec{
|
|
Name: "a",
|
|
Type: cty.Number,
|
|
},
|
|
nil,
|
|
cty.NumberIntVal(1),
|
|
0,
|
|
},
|
|
{
|
|
"a = true\n",
|
|
&AttrSpec{
|
|
Name: "a",
|
|
Type: cty.Number,
|
|
},
|
|
nil,
|
|
cty.UnknownVal(cty.Number),
|
|
1, // incorrect type - number required.
|
|
},
|
|
{
|
|
``,
|
|
&AttrSpec{
|
|
Name: "a",
|
|
Type: cty.Number,
|
|
Required: true,
|
|
},
|
|
nil,
|
|
cty.NullVal(cty.Number),
|
|
1, // attribute "a" is required
|
|
},
|
|
|
|
{
|
|
`
|
|
b {
|
|
}
|
|
`,
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: ObjectSpec{},
|
|
},
|
|
nil,
|
|
cty.EmptyObjectVal,
|
|
0,
|
|
},
|
|
{
|
|
``,
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: ObjectSpec{},
|
|
},
|
|
nil,
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
0,
|
|
},
|
|
{
|
|
"a {}\n",
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: ObjectSpec{},
|
|
},
|
|
nil,
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
1, // blocks of type "a" are not supported
|
|
},
|
|
{
|
|
``,
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: ObjectSpec{},
|
|
Required: true,
|
|
},
|
|
nil,
|
|
cty.NullVal(cty.DynamicPseudoType),
|
|
1, // a block of type "b" is required
|
|
},
|
|
{
|
|
`
|
|
b {}
|
|
b {}
|
|
`,
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: ObjectSpec{},
|
|
Required: true,
|
|
},
|
|
nil,
|
|
cty.EmptyObjectVal,
|
|
1, // only one "b" block is allowed
|
|
},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
|
|
file, parseDiags := zclsyntax.ParseConfig([]byte(test.config), "", zcl.Pos{Line: 1, Column: 1, Byte: 0})
|
|
body := file.Body
|
|
got, valDiags := Decode(body, test.spec, test.ctx)
|
|
|
|
var diags zcl.Diagnostics
|
|
diags = append(diags, parseDiags...)
|
|
diags = append(diags, valDiags...)
|
|
|
|
if len(diags) != test.diagCount {
|
|
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount)
|
|
for _, diag := range diags {
|
|
t.Logf(" - %s", diag.Error())
|
|
}
|
|
}
|
|
|
|
if !got.RawEquals(test.want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSourceRange(t *testing.T) {
|
|
tests := []struct {
|
|
config string
|
|
spec Spec
|
|
want zcl.Range
|
|
}{
|
|
{
|
|
"a = 1\n",
|
|
&AttrSpec{
|
|
Name: "a",
|
|
},
|
|
zcl.Range{
|
|
Start: zcl.Pos{Line: 1, Column: 5, Byte: 4},
|
|
End: zcl.Pos{Line: 1, Column: 6, Byte: 5},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
b {
|
|
a = 1
|
|
}
|
|
`,
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: &AttrSpec{
|
|
Name: "a",
|
|
},
|
|
},
|
|
zcl.Range{
|
|
Start: zcl.Pos{Line: 3, Column: 7, Byte: 11},
|
|
End: zcl.Pos{Line: 3, Column: 8, Byte: 12},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
b {
|
|
c {
|
|
a = 1
|
|
}
|
|
}
|
|
`,
|
|
&BlockSpec{
|
|
TypeName: "b",
|
|
Nested: &BlockSpec{
|
|
TypeName: "c",
|
|
Nested: &AttrSpec{
|
|
Name: "a",
|
|
},
|
|
},
|
|
},
|
|
zcl.Range{
|
|
Start: zcl.Pos{Line: 4, Column: 9, Byte: 19},
|
|
End: zcl.Pos{Line: 4, Column: 10, Byte: 20},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
|
|
file, diags := zclsyntax.ParseConfig([]byte(test.config), "", zcl.Pos{Line: 1, Column: 1, Byte: 0})
|
|
if len(diags) != 0 {
|
|
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), 0)
|
|
for _, diag := range diags {
|
|
t.Logf(" - %s", diag.Error())
|
|
}
|
|
}
|
|
body := file.Body
|
|
|
|
got := SourceRange(body, test.spec)
|
|
|
|
if !reflect.DeepEqual(got, test.want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|