kubernetes/test/utils/ktesting/errorcontext.go

252 lines
7.4 KiB
Go
Raw Normal View History

/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ktesting
import (
"errors"
"fmt"
"strings"
)
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
// Deprecated: use tCtx.WithError instead
func WithError(tCtx TContext, err *error) (TContext, func()) {
return tCtx.WithError(err)
}
// WithError creates a context where test failures are collected and stored in
// the provided error instance when the caller is done. Use it like this:
//
// func doSomething(tCtx ktesting.TContext) (finalErr error) {
// tCtx, finalize := WithError(tCtx, &finalErr)
// defer finalize()
// ...
// tCtx.Fatal("some failure")
//
// Any error already stored in the variable will get overwritten by finalize if
// there were test failures, otherwise the variable is left unchanged.
// If there were multiple test errors, then the error will wrap all of
// them with errors.Join.
//
// Test failures are not propagated to the parent context.
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
// WithRESTConfig initializes all client-go clients with new clients
// created for the config. The current test name gets included in the UserAgent.
func (tc *TC) WithError(err *error) (TContext, func()) {
tc = tc.clone()
tc.capture = &capture{}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
return tc, func() {
// Recover has to be called in the deferred function. When called inside
// a function called by a deferred function (like finalize below), it
// returns nil.
if e := recover(); e != nil {
if _, ok := e.(fatalWithError); !ok {
// Not our own panic, pass it on instead of setting the error.
panic(e)
}
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.finalize(err)
}
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) finalize(err *error) {
tc.capture.mutex.Lock()
defer tc.capture.mutex.Unlock()
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
errs := tc.capture.errors
if tc.capture.failed && len(errs) == 0 {
errs = []error{errFailedWithNoExplanation}
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
if len(errs) == 0 {
return
}
*err = failures{errors.Join(errs...)}
}
type failures struct {
error
}
func (e failures) GomegaString() string {
// We don't need to repeat the string. Errors already get formatted once by Gomega itself,
// then it calls GomegaString for a summary that isn't necessary anymore.
return ""
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
// buildHeader handles:
// - "ERROR:<non-empty prefix><optional header><suffix>" -> use both prefix and suffix when we have a header, otherwise just the suffix
// - "<empty prefix><optional header><suffix>" -> use suffix only if we have a header
func (tc *TC) buildHeader(prefix, suffix string) string {
if tc.perTestHeader != nil {
return prefix + tc.perTestHeader() + suffix
}
if prefix != "" {
return suffix
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
return ""
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
// indent either indents all follow-up lines or all lines including the first one.
func indent(msg string, all bool) string {
header := ""
if all {
header = "\t"
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
return header + strings.ReplaceAll(msg, "\n", "\n\t")
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Skip(args ...any) {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintln.
msg := strings.TrimSpace(fmt.Sprintln(args...))
tc.TB().Skip("SKIP:", tc.buildHeader(" ", " ")+tc.steps+indent(msg, false))
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Skipf(format string, args ...any) {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintf.
msg := strings.TrimSpace(fmt.Sprintf(format, args...))
tc.TB().Skip("SKIP:", tc.buildHeader(" ", " ")+tc.steps+indent(msg, false))
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Log(args ...any) {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintln.
msg := strings.TrimSpace(fmt.Sprintln(args...))
tc.TB().Log(tc.buildHeader("", " ") + tc.steps + indent(msg, false))
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Logf(format string, args ...any) {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintf.
msg := strings.TrimSpace(fmt.Sprintf(format, args...))
tc.TB().Log(tc.buildHeader("", " ") + tc.steps + indent(msg, false))
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Error(args ...any) {
if tc.capture == nil {
tc.Helper()
msg := strings.TrimSpace(fmt.Sprintln(args...))
// ERROR *before* header to make it stand out as failure.
tc.TB().Error("ERROR:" + tc.buildHeader(" ", "\n") + indent(tc.steps+msg, true))
return
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.capture.mutex.Lock()
defer tc.capture.mutex.Unlock()
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
// Gomega adds a leading newline in https://github.com/onsi/gomega/blob/f804ac6ada8d36164ecae0513295de8affce1245/internal/gomega.go#L37
// Let's strip that at start and end because ktesting will make errors
// stand out more with the "ERROR" prefix, so there's no need for additional
// line breaks. Besides, Sprintln (required for `go vet printf`) also
// adds a trailing newline that we don't want.
msg := strings.TrimSpace(fmt.Sprintln(args...))
tc.capture.errors = append(tc.capture.errors, errors.New(tc.steps+msg))
tc.capture.failed = true
}
func (tc *TC) Errorf(format string, args ...any) {
if tc.capture == nil {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintln.
msg := strings.TrimSpace(fmt.Sprintf(format, args...))
// ERROR *before* header to make it stand out as failure.
tc.TB().Error("ERROR:" + tc.buildHeader(" ", "\n") + indent(tc.steps+msg, true))
return
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.capture.mutex.Lock()
defer tc.capture.mutex.Unlock()
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
msg := strings.TrimSpace(fmt.Sprintf(format, args...))
tc.capture.errors = append(tc.capture.errors, errors.New(tc.steps+msg))
tc.capture.failed = true
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Fail() {
if tc.capture == nil {
tc.TB().Fail()
return
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.capture.mutex.Lock()
defer tc.capture.mutex.Unlock()
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.capture.failed = true
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) FailNow() {
if tc.capture == nil {
tc.TB().FailNow()
return
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.capture.mutex.Lock()
defer tc.capture.mutex.Unlock()
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
tc.capture.failed = true
panic(failed)
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Failed() bool {
if tc.capture == nil {
return tc.TB().Failed()
}
tc.capture.mutex.Lock()
defer tc.capture.mutex.Unlock()
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
return tc.capture.failed
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Fatal(args ...any) {
if tc.capture == nil {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintln.
msg := strings.TrimSpace(fmt.Sprintln(args...))
// FATAL ERROR *before* header to make it stand out as failure.
tc.TB().Fatal("FATAL ERROR:" + tc.buildHeader(" ", "\n") + indent(tc.steps+msg, true))
}
tc.Error(args...)
tc.FailNow()
}
ktesting: reimplement without interface The original implementation was inspired by how context.Context is handled via wrapping a parent context. That approach had several issues: - It is useful to let users call methods (e.g. tCtx.ExpectNoError) instead of ktesting functions with a tCtx parameters, but that only worked if all implementations of the interface implemented that set of methods. This made extending those methods cumbersome (see the commit which added Require+Assert) and could potentially break implementations of the interface elsewhere, defeating part of the motivation for having the interface in the first place. - It was hard to see how the different TContext wrappers cooperated with each other. - Layering injection of "ERROR" and "FATAL ERROR" on top of prefixing with the klog header caused post-processing of a failed unit test to remove that line because it looked like log output. Other log output lines where kept because they were not indented. - In Go <=1.25, the `go vet sprintf` check only works for functions and methods if they get called directly and themselves directly pass their parameters on to fmt.Sprint. The check does not work when calling methods through an interface. Support for that is coming in Go 1.26, but will depend on bumping the Go version also in go.mod and thus may not be immediately possible in Kubernetes. - Interface documentation in https://pkg.go.dev/k8s.io/kubernetes@v1.34.2/test/utils/ktesting#TContext is a monolithic text block. Documentation for methods is more readable and allows referencing those methods with [] (e.g. [TC.Errorf] works, [TContext.Errorf] didn't). The revised implementation is a single struct with (almost) no exported fields. The two exceptions (embedded context.Context and TB) are useful because it avoids having to write wrappers for several functions resp. necessary because Helper cannot be wrapped. Like a logr.LogSink, With* methods can make a shallow copy and then change some fields in the cloned instance. The former `ktesting.TContext` interface is now a type alias for `*ktesting.TC`. This ensures that existing code using ktesting doesn't need to be updated and because that code is a bit more compact (`tCtx ktesting.TContext` instead of `tCtx *ktesting.TContext` when not using such an alias). Hiding that it is a pointer might discourage accessing the exported fields because it looks like an interface. Output gets fixed and improved such that: - "FATAL ERROR" and "ERROR" are at the start of the line, followed by the klog header. - The failure message follows in the next line. - Continuation lines are always indented. The set of methods exposed via TB is now a bit more complete (Attr, Chdir). All former stand-alone With* functions are now also available as methods and should be used instead of the functions. Those will be removed. Linting of log calls now works and found some issues.
2025-12-08 02:19:51 -05:00
func (tc *TC) Fatalf(format string, args ...any) {
if tc.capture == nil {
tc.Helper()
// Enable `go vet printf` by directly calling fmt.Sprintf.
msg := strings.TrimSpace(fmt.Sprintf(format, args...))
// FATAL ERROR *before* header to make it stand out as failure.
tc.TB().Fatal("FATAL ERROR:" + tc.buildHeader(" ", "\n") + indent(tc.steps+msg, true))
return
}
tc.Errorf(format, args...)
tc.FailNow()
}
// fatalWithError is the internal type that should never get propagated up. The
// only case where that can happen is when the developer forgot to call
// finalize via defer. The string explains that, in case that developers get to
// see it.
type fatalWithError string
const failed = fatalWithError("WithError TContext encountered a fatal error, but the finalize function was not called via defer as it should have been.")
var errFailedWithNoExplanation = errors.New("WithError context was marked as failed without recording an error")