hcl/merged_test.go
Martin Atkins 6c4344623b Unfold the "hcl" directory up into the root
The main HCL package is more visible this way, and so it's easier than
having to pick it out from dozens of other package directories.
2019-09-09 16:08:19 -07:00

684 lines
12 KiB
Go

package hcl
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))
}
})
}
}
func TestMergeBodiesPartialContent(t *testing.T) {
tests := []struct {
Bodies []Body
Schema *BodySchema
WantContent *BodyContent
WantRemain Body
DiagCount int
}{
{
[]Body{},
&BodySchema{},
&BodyContent{
Attributes: map[string]*Attribute{},
},
mergedBodies{},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"name", "age"},
},
},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{
"name": &Attribute{
Name: "name",
NameRange: Range{Filename: "first"},
},
},
},
mergedBodies{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"age"},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"name", "age"},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{"name", "pizza"},
},
},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{
"name": &Attribute{
Name: "name",
NameRange: Range{Filename: "first"},
},
},
},
mergedBodies{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"age"},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{"pizza"},
},
},
1,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"name", "age"},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{"pizza", "soda"},
},
},
&BodySchema{
Attributes: []AttributeSchema{
{
Name: "name",
},
{
Name: "soda",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{
"name": &Attribute{
Name: "name",
NameRange: Range{Filename: "first"},
},
"soda": &Attribute{
Name: "soda",
NameRange: Range{Filename: "second"},
},
},
},
mergedBodies{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{"age"},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{"pizza"},
},
},
0,
},
{
[]Body{
&testMergedBodiesVictim{
Name: "first",
HasBlocks: map[string]int{
"pizza": 1,
},
},
&testMergedBodiesVictim{
Name: "second",
HasBlocks: map[string]int{
"pizza": 1,
"soda": 2,
},
},
},
&BodySchema{
Blocks: []BlockHeaderSchema{
{
Type: "pizza",
},
},
},
&BodyContent{
Attributes: map[string]*Attribute{},
Blocks: Blocks{
{
Type: "pizza",
DefRange: Range{Filename: "first"},
},
{
Type: "pizza",
DefRange: Range{Filename: "second"},
},
},
},
mergedBodies{
&testMergedBodiesVictim{
Name: "first",
HasAttributes: []string{},
HasBlocks: map[string]int{},
},
&testMergedBodiesVictim{
Name: "second",
HasAttributes: []string{},
HasBlocks: map[string]int{
"soda": 2,
},
},
},
0,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
merged := MergeBodies(test.Bodies)
got, gotRemain, diags := merged.PartialContent(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.WantContent) {
t.Errorf("wrong content result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.WantContent))
}
if !reflect.DeepEqual(gotRemain, test.WantRemain) {
t.Errorf("wrong remaining result\ngot: %s\nwant: %s", spew.Sdump(gotRemain), spew.Sdump(test.WantRemain))
}
})
}
}
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) {
remain := &testMergedBodiesVictim{
Name: v.Name,
HasAttributes: []string{},
}
hasAttrs := map[string]struct{}{}
for _, n := range v.HasAttributes {
hasAttrs[n] = struct{}{}
var found bool
for _, attrS := range schema.Attributes {
if n == attrS.Name {
found = true
break
}
}
if !found {
remain.HasAttributes = append(remain.HasAttributes, n)
}
}
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,
})
}
}
remain.HasBlocks = map[string]int{}
for n := range v.HasBlocks {
var found bool
for _, blockS := range schema.Blocks {
if blockS.Type == n {
found = true
break
}
}
if !found {
remain.HasBlocks[n] = v.HasBlocks[n]
}
}
}
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, remain, diags
}
func (v *testMergedBodiesVictim) JustAttributes() (Attributes, 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,
}
}