Index inherited step membership

This commit is contained in:
2026-10-09 03:56:29 +00:00
parent d32c94eca3
commit c8546ae599
3 changed files with 159 additions and 12 deletions

View File

@@ -2,6 +2,8 @@ package engine
import (
"io/fs"
"reflect"
"slices"
"testing"
"testing/fstest"
@@ -85,6 +87,120 @@ func TestInheritedOriginsKeepSourcePathsPresenceAndIndependentReplacement(t *tes
}
}
func TestProjectedInheritanceStepsMatchScanAndRetainOwnership(t *testing.T) {
sourcePath := Keys("source", "a/b", "01")
firstLocations := []Location{{SourceID: "first;:source", Line: -1, Column: 2}}
secondLocations := []Location{
{SourceID: "second:;source", Line: 3, Column: -4},
{SourceID: "", Line: -5, Column: 6},
}
firstTransfer := inheritanceTransfer{
from: Keys("source"), to: Keys("middle"), reference: Keys("middle", "extends"), locations: firstLocations,
}
secondTransfer := inheritanceTransfer{
from: Keys("middle"), to: Keys("target"), reference: Keys("target", "extends"), locations: secondLocations,
}
firstTrace := &inheritanceTrace{transfer: &firstTransfer}
chain := &inheritanceTrace{previous: firstTrace, transfer: &secondTransfer}
firstStep := InheritanceStep{
From: sourcePath, To: Keys("middle", "a/b", "01"), Reference: firstTransfer.reference, Locations: firstLocations,
}
duplicateFirstTransfer := firstTransfer
duplicateSecondTransfer := secondTransfer
duplicateChain := &inheritanceTrace{
previous: &inheritanceTrace{transfer: &duplicateFirstTransfer},
transfer: &duplicateSecondTransfer,
}
reversedLocations := []Location{secondLocations[1], secondLocations[0]}
reorderedSecondTransfer := secondTransfer
reorderedSecondTransfer.locations = reversedLocations
reorderedChain := &inheritanceTrace{
previous: &inheritanceTrace{transfer: &duplicateFirstTransfer},
transfer: &reorderedSecondTransfer,
}
origin := Origin{
Inheritance: []InheritanceStep{firstStep, firstStep},
contributors: []inheritanceContributor{
{source: sourcePath, trace: chain},
{source: sourcePath, trace: duplicateChain},
{source: sourcePath, trace: reorderedChain},
{source: sourcePath, trace: chain},
},
}
want := projectInheritanceStepsByScan(origin)
got := projectInheritanceSteps(origin)
if !reflect.DeepEqual(got, want) {
t.Fatalf("projected steps differ from ordered scan:\n got: %#v\nwant: %#v", got, want)
}
if len(got) != 4 || got[0].From.String() != "/source/a~1b/01" || got[0].To.String() != "/middle/a~1b/01" ||
!reflect.DeepEqual(got[0], got[1]) || got[2].To.String() != "/target/a~1b/01" ||
!reflect.DeepEqual(got[2].Locations, secondLocations) || !reflect.DeepEqual(got[3].Locations, reversedLocations) {
t.Fatalf("unexpected duplicate/order/escape projection: %#v", got)
}
got[0].From.segments[0].key = "mutated"
got[2].Locations[0].SourceID = "mutated"
if repeated := projectInheritanceSteps(origin); !reflect.DeepEqual(repeated, want) {
t.Fatalf("returned steps mutated retained evidence:\n got: %#v\nwant: %#v", repeated, want)
}
}
func TestInheritanceLocationsKeyEncodesOrderedLengthPrefixedValues(t *testing.T) {
if inheritanceLocationsKey(nil) != inheritanceLocationsKey([]Location{}) {
t.Fatal("nil and empty location lists should encode equally")
}
values := [][]Location{
{{SourceID: "a;1;-2;", Line: -3, Column: 4}},
{{SourceID: "a", Line: 1, Column: -2}, {SourceID: ";1;-2;", Line: -3, Column: 4}},
{{SourceID: ";1;-2;", Line: -3, Column: 4}, {SourceID: "a", Line: 1, Column: -2}},
{{SourceID: "é", Line: -3, Column: 4}},
{{SourceID: "e", Line: -3, Column: 4}},
}
keys := make(map[string]int, len(values))
for index, locations := range values {
key := inheritanceLocationsKey(locations)
if previous, found := keys[key]; found {
t.Fatalf("location lists %d and %d encoded identically", previous, index)
}
keys[key] = index
}
}
func projectInheritanceStepsByScan(origin Origin) []InheritanceStep {
steps := cloneInheritanceSteps(origin.Inheritance)
for _, contributor := range origin.contributors {
var reverse []*inheritanceTrace
for link := contributor.trace; link != nil; link = link.previous {
reverse = append(reverse, link)
}
current := contributor.source
for index := len(reverse) - 1; index >= 0; index-- {
transfer := reverse[index].transfer
relative, ok := relativeHistoryPath(transfer.from, current)
if !ok {
continue
}
next := appendInheritancePath(transfer.to, relative)
step := InheritanceStep{
From: current.clone(), To: next.clone(), Reference: transfer.reference.clone(),
Locations: cloneLocations(transfer.locations),
}
if !slices.ContainsFunc(steps, func(existing InheritanceStep) bool {
return existing.From.String() == step.From.String() &&
existing.To.String() == step.To.String() &&
existing.Reference.String() == step.Reference.String() &&
slices.Equal(existing.Locations, step.Locations)
}) {
steps = append(steps, step)
}
current = next
}
}
return steps
}
func TestInheritedOriginsRetainChainsAndNestedDestinations(t *testing.T) {
first := parseCompositionDocument(t, "source-1", `first:
enabled: false