hcl/zcl/merged_test.go
Martin Atkins 8654cf0361 Body.MissingItemRange method
When producing diagnostics about missing attributes or bodies it's
necessary to have a range representing the place where the missing thing
might be inserted.

There's not always a single reasonable value for this, so some liberty
can be taken about what exactly is returned as long as it's somewhere
the user can relate back to the construct producing the error.
2017-05-21 11:46:58 -07:00

447 lines
7.5 KiB
Go

package zcl
import (
"fmt"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
func TestMergedBodiesContent(t *testing.T) {
tests := []struct {
Bodies []Body
Schema *BodySchema
Want *BodyContent
DiagCount int
}{
{
[]Body{},
&BodySchema{},
&BodyContent{
Attributes: map[string]*Attribute{},
},
0,
},
{
[]Body{},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
},
0,
},
{
[]Body{},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
Required: true,
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
},
1,
},
{
[]Body{
&testMergedBodiesVictim{
HasAttributes: []string{"name"},
},
},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{
"name": &Attribute{
Name: "name",
},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"name"},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{"name"},
},
},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{
"name": &Attribute{
Name: "name",
NameRange: Range{Filename: "first"},
},
},
},
1,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"name"},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{"age"},
},
},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
{
Name: "age",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{
"name": &Attribute{
Name: "name",
NameRange: Range{Filename: "first"},
},
"age": &Attribute{
Name: "age",
NameRange: Range{Filename: "second"},
},
},
},
0,
},
{
[]Body{},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
HasBlocks: map[string]int{
"pizza": 1,
},
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
Blocks: Blocks{
{
Type: "pizza",
},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
HasBlocks: map[string]int{
"pizza": 2,
},
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
Blocks: Blocks{
{
Type: "pizza",
},
{
Type: "pizza",
},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasBlocks: map[string]int{
"pizza": 1,
},
},
&testMergedBodiesVictim{
Name: "second",
HasBlocks: map[string]int{
"pizza": 1,
},
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
Blocks: Blocks{
{
Type: "pizza",
DefRange: Range{Filename: "first"},
},
{
Type: "pizza",
DefRange: Range{Filename: "second"},
},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
},
&testMergedBodiesVictim{
Name: "second",
HasBlocks: map[string]int{
"pizza": 2,
},
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
Blocks: Blocks{
{
Type: "pizza",
DefRange: Range{Filename: "second"},
},
{
Type: "pizza",
DefRange: Range{Filename: "second"},
},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasBlocks: map[string]int{
"pizza": 2,
},
},
&testMergedBodiesVictim{
Name: "second",
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
Blocks: Blocks{
{
Type: "pizza",
DefRange: Range{Filename: "first"},
},
{
Type: "pizza",
DefRange: Range{Filename: "first"},
},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
},
&testMergedBodiesVictim{
Name: "second",
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
},
0,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
merged := MergeBodies(test.Bodies)
got, diags := merged.Content(test.Schema)
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)
}
}
if !reflect.DeepEqual(got, test.Want) {
t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.Want))
}
})
}
}
type testMergedBodiesVictim struct {
Name string
HasAttributes []string
HasBlocks map[string]int
DiagCount int
}
func (v *testMergedBodiesVictim) Content(schema *BodySchema) (*BodyContent, Diagnostics) {
c, _, d := v.PartialContent(schema)
return c, d
}
func (v *testMergedBodiesVictim) PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics) {
hasAttrs := map[string]struct{}{}
for _, n := range v.HasAttributes {
hasAttrs[n] = struct{}{}
}
content := &BodyContent{
Attributes: map[string]*Attribute{},
}
rng := Range{
Filename: v.Name,
}
for _, attrS := range schema.Attributes {
_, has := hasAttrs[attrS.Name]
if has {
content.Attributes[attrS.Name] = &Attribute{
Name: attrS.Name,
NameRange: rng,
}
}
}
if v.HasBlocks != nil {
for _, blockS := range schema.Blocks {
num := v.HasBlocks[blockS.Type]
for i := 0; i < num; i++ {
content.Blocks = append(content.Blocks, &Block{
Type: blockS.Type,
DefRange: rng,
})
}
}
}
diags := make(Diagnostics, v.DiagCount)
for i := range diags {
diags[i] = &Diagnostic{
Severity: DiagError,
Summary: fmt.Sprintf("Fake diagnostic %d", i),
Detail: "For testing only.",
Context: &rng,
}
}
return content, emptyBody, diags
}
func (v *testMergedBodiesVictim) JustAttributes() (map[string]*Attribute, Diagnostics) {
attrs := make(map[string]*Attribute)
rng := Range{
Filename: v.Name,
}
for _, name := range v.HasAttributes {
attrs[name] = &Attribute{
Name: name,
NameRange: rng,
}
}
diags := make(Diagnostics, v.DiagCount)
for i := range diags {
diags[i] = &Diagnostic{
Severity: DiagError,
Summary: fmt.Sprintf("Fake diagnostic %d", i),
Detail: "For testing only.",
Context: &rng,
}
}
return attrs, diags
}
func (v *testMergedBodiesVictim) MissingItemRange() Range {
return Range{
Filename: v.Name,
}
}