102 lines
3.1 KiB
Go
102 lines
3.1 KiB
Go
package engine
|
|
|
|
import (
|
|
"bytes"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
|
|
)
|
|
|
|
func FuzzCompositionAndOverlay(f *testing.F) {
|
|
for _, seed := range [][2]string{
|
|
{"{}\n", "{}\n"},
|
|
{"left: {value: one}\n", "right: [two]\n"},
|
|
{"shared: {left: one}\n", "shared: {right: two}\n"},
|
|
{"value: one\n", "value: two\n"},
|
|
} {
|
|
f.Add([]byte(seed[0]), []byte(seed[1]))
|
|
}
|
|
|
|
limits := yamladapter.Limits{Depth: 12, Nodes: 256, ParserDepth: 24}
|
|
f.Fuzz(func(t *testing.T, leftInput, rightInput []byte) {
|
|
if len(leftInput) > 4<<10 || len(rightInput) > 4<<10 {
|
|
return
|
|
}
|
|
left, leftIssues := yamladapter.Parse(bytes.NewReader(leftInput), "left", limits)
|
|
right, rightIssues := yamladapter.Parse(bytes.NewReader(rightInput), "right", limits)
|
|
if len(leftIssues) != 0 || len(rightIssues) != 0 {
|
|
return
|
|
}
|
|
leftBefore, rightBefore := document.Clone(left), document.Clone(right)
|
|
|
|
composed, diagnostics := composeDocuments(left, right)
|
|
if !reflect.DeepEqual(left, leftBefore) || !reflect.DeepEqual(right, rightBefore) {
|
|
t.Fatal("composition mutated an input document")
|
|
}
|
|
if len(diagnostics) == 0 {
|
|
reversed, reversedDiagnostics := composeDocuments(right, left)
|
|
if len(reversedDiagnostics) != 0 {
|
|
t.Fatalf("reversed composition failed: %#v", reversedDiagnostics)
|
|
}
|
|
if differences := compareConfigurationValues(composed.root, reversed.root); len(differences) != 0 {
|
|
t.Fatalf("composition order changed effective values: %#v", differences)
|
|
}
|
|
if mutateExistingDocumentDescendant(composed.root) {
|
|
if !reflect.DeepEqual(left, leftBefore) || !reflect.DeepEqual(right, rightBefore) {
|
|
t.Fatal("composition result aliases an input document")
|
|
}
|
|
}
|
|
}
|
|
|
|
base, baseDiagnostics := composeDocuments(left)
|
|
if len(baseDiagnostics) != 0 {
|
|
t.Fatalf("single accepted mapping did not compose: %#v", baseDiagnostics)
|
|
}
|
|
overlaid, overlayDiagnostics := overlayDocuments(base, right)
|
|
if len(overlayDiagnostics) != 0 {
|
|
t.Fatalf("accepted mappings did not overlay: %#v", overlayDiagnostics)
|
|
}
|
|
if !reflect.DeepEqual(left, leftBefore) || !reflect.DeepEqual(right, rightBefore) {
|
|
t.Fatal("overlay mutated an input document")
|
|
}
|
|
if mutateExistingDocumentDescendant(overlaid.root) {
|
|
if !reflect.DeepEqual(left, leftBefore) || !reflect.DeepEqual(right, rightBefore) {
|
|
t.Fatal("overlay result aliases an input document")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func mutateExistingDocumentDescendant(node *document.Node) bool {
|
|
if node == nil {
|
|
return false
|
|
}
|
|
// Empty containers have no existing element for the alias probe.
|
|
switch node.Kind {
|
|
case document.Mapping:
|
|
if len(node.Mapping) == 0 {
|
|
return false
|
|
}
|
|
entry := &node.Mapping[0]
|
|
if mutateExistingDocumentDescendant(entry.Value) {
|
|
return true
|
|
}
|
|
entry.Key += "-fuzz-detachment"
|
|
return true
|
|
case document.Sequence:
|
|
if len(node.Sequence) == 0 {
|
|
return false
|
|
}
|
|
if mutateExistingDocumentDescendant(node.Sequence[0]) {
|
|
return true
|
|
}
|
|
node.Sequence[0].Text += "fuzz-detachment"
|
|
return true
|
|
default:
|
|
node.Text += "fuzz-detachment"
|
|
return true
|
|
}
|
|
}
|