diff --git a/cue/testdata/disjunctions/errors.txtar b/cue/testdata/disjunctions/errors.txtar index e60605ed0..03c17244b 100644 --- a/cue/testdata/disjunctions/errors.txtar +++ b/cue/testdata/disjunctions/errors.txtar @@ -69,9 +69,7 @@ issue3581: reduced: { suberr=(code=incomplete, contains="invalid interpolation: non-concrete value", pos=[0:9]), ) c: _ - // TODO(inline): generator doesn't recognize the error here and prints - // the list with value. Probably because the erroneous node has fields. - @test(eq:todo, {list: _|_, c: _}) + @test(eq, {list: _|_, c: _}) } -- issue3576.cue -- // evalv3: disjunct of definitions with identical optional fields results in diff --git a/internal/cuetxtar/inline.go b/internal/cuetxtar/inline.go index f0f90c2bd..01353ec69 100644 --- a/internal/cuetxtar/inline.go +++ b/internal/cuetxtar/inline.go @@ -891,7 +891,12 @@ func (r *inlineRunner) runEqInline(t testing.TB, path cue.Path, val cue.Value, p } // @test(eq:todo, X) — expected-to-fail form. - // Failures are logged but not reported as test errors; a match emits a warning. + // Failures are logged but not reported as test errors. A match emits a + // warning and, under CUE_UPDATE=1 / CUE_UPDATE=force, auto-promotes the + // directive by stripping the :todo qualifier so the source diff shows + // the promotion. The signal lives in the diff (which CI / AI tooling + // already inspects) rather than in test output that would risk being + // mistaken for a failure. if pa.isTodo { if exprStr == "" { return @@ -904,6 +909,9 @@ func (r *inlineRunner) runEqInline(t testing.TB, path cue.Path, val cue.Value, p cmpErr := (&cmpCtx{baseLine: pa.baseLine}).astCmp(cue.Path{}, expr, val) if cmpErr == nil { t.Logf("WARNING: path %s: TODO eq:todo now passes — consider upgrading to @test(eq, %s)", path, exprStr) + if cuetest.UpdateGoldenFiles || cuetest.ForceUpdateGoldenFiles { + r.enqueueInlineFill(pa, promoteTodoAttr(pa)) + } } else { t.Logf("path %s: TODO eq:todo still failing: %v", path, cmpErr) } @@ -1108,6 +1116,18 @@ func attrHasSkip(raw *internal.Attr) (ver string, ok bool) { return "", false } +// promoteTodoAttr returns the replacement text for an @test(eq:todo, ...) +// attribute that now passes: the :todo qualifier is stripped from the +// directive name, leaving the rest of pa.srcAttr.Text untouched. Replacing +// the FIRST literal "eq:todo" is safe because parseTestAttr already +// verified the attribute starts with that directive. +func promoteTodoAttr(pa parsedTestAttr) string { + if pa.srcAttr == nil { + return "" + } + return strings.Replace(pa.srcAttr.Text, "eq:todo", "eq", 1) +} + // enqueueInlineFill appends a byte-level replacement for pa's @test attribute. // newAttrText is the full replacement text including the leading @. // It is a no-op when pa.srcAttr is nil (e.g. in unit tests that construct diff --git a/internal/cuetxtar/testdata/inline/eq_todo_promote.txtar b/internal/cuetxtar/testdata/inline/eq_todo_promote.txtar new file mode 100644 index 000000000..38cf2843c --- /dev/null +++ b/internal/cuetxtar/testdata/inline/eq_todo_promote.txtar @@ -0,0 +1,22 @@ +# Tests that @test(eq:todo, X) auto-promotes to @test(eq, X) when the +# todo now passes, but only under CUE_UPDATE. Plain `go test` runs are +# silent (todo failures are not test errors and a now-passing todo emits +# only a -v log line). The signal that "this todo can be retired" lives +# in the source diff produced by CUE_UPDATE rather than in test output — +# this keeps normal runs clean and avoids tripping up developers (or AI +# tooling) that scan for failures. +-- in/test.cue -- +// passing: this todo now matches; CUE_UPDATE must drop :todo. +passing: 42 @test(eq:todo, 42) + +// stillFailing: the todo does not match and remains as-is. +stillFailing: 1 @test(eq:todo, 2) +-- out/status.txt -- +update: output passes run +force: identical to update +-- out/update/test.cue -- +// passing: this todo now matches; CUE_UPDATE must drop :todo. +passing: 42 @test(eq, 42) + +// stillFailing: the todo does not match and remains as-is. +stillFailing: 1 @test(eq:todo, 2) diff --git a/internal/cuetxtar/testdata/inline/todo.txtar b/internal/cuetxtar/testdata/inline/todo.txtar index 2b88ae912..bd9d40caa 100644 --- a/internal/cuetxtar/testdata/inline/todo.txtar +++ b/internal/cuetxtar/testdata/inline/todo.txtar @@ -66,7 +66,7 @@ suppressFailingErr: 42 @test(err) @test(todo) eqTodoStillFailing: 42 @test(eq, 42) @test(eq:todo, 99) // @test(eq:todo, X) where value == X: warning logged, no failure -eqTodoPassing: 42 @test(eq, 42) @test(eq:todo, 42) +eqTodoPassing: 42 @test(eq, 42) @test(eq, 42) // @test(eq, ...) and @test(eq:todo, ...) coexist independently eqAndEqTodoCoexist: 42 @test(eq, 42) @test(eq:todo, 99) @@ -108,7 +108,7 @@ suppressFailingErr: 42 @test(err) @test(todo) eqTodoStillFailing: 42 @test(eq, 42) @test(eq:todo, 99) // @test(eq:todo, X) where value == X: warning logged, no failure -eqTodoPassing: 42 @test(eq, 42) @test(eq:todo, 42) +eqTodoPassing: 42 @test(eq, 42) @test(eq, 42) // @test(eq, ...) and @test(eq:todo, ...) coexist independently eqAndEqTodoCoexist: 42 @test(eq, 42) @test(eq:todo, 99)