Tests for what an operand's draw actually fixes
Widening the fence used containment for both kinds of hold, which is right for a level a path reads and wrong for a sibling a calc reads. A path may read into anything the level contains; an operand's draw fixes only the value it renders. So containment rejects a reference to a sibling the operand never renders, which loads fine today and cannot disagree, and misses a reference reaching what the operand renders through, which disagreed in 20 of 40 draws. Also pins the repeated reference arm, the one kind that reaches the repeat check by passing the per-arm checks rather than falling through them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+29
-6
@@ -158,12 +158,35 @@ func TestACalcOperandIsHeldAgainstEveryRoute(t *testing.T) {
|
|||||||
t.Errorf("%s: New = %v, want the second route to the operand rejected", name, err)
|
t.Errorf("%s: New = %v, want the second route to the operand rejected", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The spellings that read the one draw stay legal: the operand itself, and a
|
// A reference reaching what the operand renders *through* is a second route
|
||||||
// bare token naming it.
|
// too, however the operand comes by its value.
|
||||||
if _, err := New([]string{writeData(t, map[string]string{
|
_, err := New([]string{writeData(t, map[string]string{
|
||||||
"ok": `{"format":"{net} x 2 = {calc(net * 2, 2)}","net":["10.00","20.00"]}`,
|
"common": `["1","2"]`,
|
||||||
})}); err != nil {
|
"cat": `{"format":"{calc(net * 2, 2)} {..common}","net":{"format":"{..common}"}}`,
|
||||||
t.Errorf("New = %v, want a format that only reads the operand accepted", err)
|
})})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "a {calc()} also reads") {
|
||||||
|
t.Errorf("New = %v, want the reference to what the operand renders rejected", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted := map[string]map[string]string{
|
||||||
|
// The spellings that read the one draw: the operand itself, and a bare token.
|
||||||
|
"only the operand and a bare token": {
|
||||||
|
"cat": `{"format":"{net} x 2 = {calc(net * 2, 2)}","net":["10.00","20.00"]}`,
|
||||||
|
},
|
||||||
|
// An operand's draw fixes the value it renders and nothing else, so a sibling
|
||||||
|
// its format never renders cannot disagree with it. A path head differs: a
|
||||||
|
// path may read into anything the level contains, which is why that half of
|
||||||
|
// the fence still covers containment.
|
||||||
|
"a reference to a sibling the operand never renders": {
|
||||||
|
"cat": `{"format":"{calc(net * 2, 2)} {unit}",` +
|
||||||
|
`"net":{"format":"{v}","v":["1","2"],"spare":["kg","lb"]},` +
|
||||||
|
`"unit":{"format":"{..cat.net.spare}"}}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, files := range accepted {
|
||||||
|
if _, err := New([]string{writeData(t, files)}); err != nil {
|
||||||
|
t.Errorf("%s: New = %v, want it accepted", name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,12 @@ func TestNewErrors(t *testing.T) {
|
|||||||
map[string]string{"a": `{"format":"{p.v|p.v}","p":{"format":"{v}","v":["1"]}}`},
|
map[string]string{"a": `{"format":"{p.v|p.v}","p":{"format":"{v}","v":["1"]}}`},
|
||||||
`arm "p.v" is repeated`,
|
`arm "p.v" is repeated`,
|
||||||
},
|
},
|
||||||
|
// A reference arm is the only kind that reaches the repeat check by passing
|
||||||
|
// the per-arm checks rather than falling through them.
|
||||||
|
"repeated reference arm": {
|
||||||
|
map[string]string{"a": `["x"]`, "b": `{"format":"{..a|..a}"}`},
|
||||||
|
`arm "..a" is repeated`,
|
||||||
|
},
|
||||||
// An arm that is broken on its own terms is reported as that, not as a
|
// An arm that is broken on its own terms is reported as that, not as a
|
||||||
// repeat: the repeat is a consequence of the real mistake.
|
// repeat: the repeat is a consequence of the real mistake.
|
||||||
"repeated arm with no path": {
|
"repeated arm with no path": {
|
||||||
|
|||||||
Reference in New Issue
Block a user