Skip to content

Commit 08d2805

Browse files
authored
Merge pull request #3336 from sttts/sttts-testing-t
🌱 Replace *testing.T with interface for integration with test frameworks
2 parents 5ae636a + d0951cc commit 08d2805

File tree

15 files changed

+141
-61
lines changed

15 files changed

+141
-61
lines changed

sdk/testing/helpers/eventually.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package helpers
1818

1919
import (
2020
"fmt"
21-
"testing"
2221
"time"
2322

2423
"github.com/stretchr/testify/require"
@@ -33,7 +32,7 @@ import (
3332
// Eventually asserts that given condition will be met in waitFor time, periodically checking target function
3433
// each tick. In addition to require.Eventually, this function t.Logs the reason string value returned by the condition
3534
// function (eventually after 20% of the wait time) to aid in debugging.
36-
func Eventually(t *testing.T, condition func() (success bool, reason string), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
35+
func Eventually(t TestingT, condition func() (success bool, reason string), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
3736
t.Helper()
3837

3938
var last string
@@ -55,7 +54,7 @@ func Eventually(t *testing.T, condition func() (success bool, reason string), wa
5554
}
5655

5756
// EventuallyReady asserts that the object returned by getter() eventually has a ready condition.
58-
func EventuallyReady(t *testing.T, getter func() (conditions.Getter, error), msgAndArgs ...interface{}) {
57+
func EventuallyReady(t TestingT, getter func() (conditions.Getter, error), msgAndArgs ...interface{}) {
5958
t.Helper()
6059
EventuallyCondition(t, getter, Is(conditionsv1alpha1.ReadyCondition), msgAndArgs...)
6160
}
@@ -117,7 +116,7 @@ func (c *ConditionEvaluator) WithReason(reason string) *ConditionEvaluator {
117116
}
118117

119118
// EventuallyCondition asserts that the object returned by getter() eventually has a condition that matches the evaluator.
120-
func EventuallyCondition(t *testing.T, getter func() (conditions.Getter, error), evaluator *ConditionEvaluator, msgAndArgs ...interface{}) {
119+
func EventuallyCondition(t TestingT, getter func() (conditions.Getter, error), evaluator *ConditionEvaluator, msgAndArgs ...interface{}) {
121120
t.Helper()
122121
Eventually(t, func() (bool, string) {
123122
obj, err := getter()

sdk/testing/helpers/testing.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright 2025 The KCP Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package helpers
18+
19+
// TestingT is implemented by *testing.T and potentially other test frameworks.
20+
type TestingT interface {
21+
Cleanup(func())
22+
Errorf(format string, args ...any)
23+
FailNow()
24+
Helper()
25+
Log(args ...any)
26+
Logf(format string, args ...any)
27+
}

sdk/testing/kcp.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"context"
2121
"embed"
2222
"path/filepath"
23-
"testing"
2423

2524
"github.com/stretchr/testify/require"
2625

@@ -34,7 +33,7 @@ var fs embed.FS
3433

3534
// PrivateKcpServer returns a new kcp server fixture managing a new
3635
// server process that is not intended to be shared between tests.
37-
func PrivateKcpServer(t *testing.T, options ...kcptestingserver.Option) kcptestingserver.RunningServer {
36+
func PrivateKcpServer(t TestingT, options ...kcptestingserver.Option) kcptestingserver.RunningServer {
3837
t.Helper()
3938

4039
serverName := "main"
@@ -71,7 +70,7 @@ func PrivateKcpServer(t *testing.T, options ...kcptestingserver.Option) kcptesti
7170
// `--kcp-kubeconfig` or `--use-default-kcp-server` is supplied to the test
7271
// runner. Otherwise a test-managed server will be started. Only tests
7372
// that are known to be hermetic are compatible with shared fixture.
74-
func SharedKcpServer(t *testing.T) kcptestingserver.RunningServer {
73+
func SharedKcpServer(t TestingT) kcptestingserver.RunningServer {
7574
t.Helper()
7675

7776
setupExternal()
@@ -122,7 +121,7 @@ func SharedKcpServer(t *testing.T) kcptestingserver.RunningServer {
122121
return f[c.Name]
123122
}
124123

125-
func createClientCA(t *testing.T) (string, string) {
124+
func createClientCA(t TestingT) (string, string) {
126125
clientCADir := t.TempDir()
127126
_, err := crypto.MakeSelfSignedCA(
128127
filepath.Join(clientCADir, "client-ca.crt"),

sdk/testing/server/external.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"math/big"
2929
"os"
3030
"path/filepath"
31-
"testing"
3231
"time"
3332

3433
"github.com/stretchr/testify/require"
@@ -87,7 +86,7 @@ func (s *externalKCPServer) CADirectory() string {
8786
return s.caDir
8887
}
8988

90-
func (s *externalKCPServer) ClientCAUserConfig(t *testing.T, config *rest.Config, name string, groups ...string) *rest.Config {
89+
func (s *externalKCPServer) ClientCAUserConfig(t TestingT, config *rest.Config, name string, groups ...string) *rest.Config {
9190
return clientCAUserConfig(t, config, s.caDir, name, groups...)
9291
}
9392

@@ -104,7 +103,7 @@ func (s *externalKCPServer) RawConfig() (clientcmdapi.Config, error) {
104103
}
105104

106105
// BaseConfig returns a rest.Config for the "base" context. Client-side throttling is disabled (QPS=-1).
107-
func (s *externalKCPServer) BaseConfig(t *testing.T) *rest.Config {
106+
func (s *externalKCPServer) BaseConfig(t TestingT) *rest.Config {
108107
t.Helper()
109108

110109
raw, err := s.cfg.RawConfig()
@@ -122,14 +121,14 @@ func (s *externalKCPServer) BaseConfig(t *testing.T) *rest.Config {
122121
}
123122

124123
// RootShardSystemMasterBaseConfig returns a rest.Config for the "shard-base" context. Client-side throttling is disabled (QPS=-1).
125-
func (s *externalKCPServer) RootShardSystemMasterBaseConfig(t *testing.T) *rest.Config {
124+
func (s *externalKCPServer) RootShardSystemMasterBaseConfig(t TestingT) *rest.Config {
126125
t.Helper()
127126

128127
return s.ShardSystemMasterBaseConfig(t, corev1alpha1.RootShard)
129128
}
130129

131130
// ShardSystemMasterBaseConfig returns a rest.Config for the "shard-base" context of the given shard. Client-side throttling is disabled (QPS=-1).
132-
func (s *externalKCPServer) ShardSystemMasterBaseConfig(t *testing.T, shard string) *rest.Config {
131+
func (s *externalKCPServer) ShardSystemMasterBaseConfig(t TestingT, shard string) *rest.Config {
133132
t.Helper()
134133

135134
cfg, found := s.shardCfgs[shard]
@@ -155,7 +154,7 @@ func (s *externalKCPServer) ShardNames() []string {
155154
return sets.StringKeySet(s.shardCfgs).List()
156155
}
157156

158-
func (s *externalKCPServer) Artifact(t *testing.T, producer func() (runtime.Object, error)) {
157+
func (s *externalKCPServer) Artifact(t TestingT, producer func() (runtime.Object, error)) {
159158
t.Helper()
160159
artifact(t, s, producer)
161160
}
@@ -183,7 +182,7 @@ func loadKubeConfig(kubeconfigPath, contextName string) (clientcmd.ClientConfig,
183182

184183
// clientCAUserConfig returns a config based on a dynamically created client certificate.
185184
// The returned client CA is signed by "test/e2e/framework/client-ca.crt".
186-
func clientCAUserConfig(t *testing.T, cfg *rest.Config, clientCAConfigDirectory, username string, groups ...string) *rest.Config {
185+
func clientCAUserConfig(t TestingT, cfg *rest.Config, clientCAConfigDirectory, username string, groups ...string) *rest.Config {
187186
t.Helper()
188187
caBytes, err := os.ReadFile(filepath.Join(clientCAConfigDirectory, "client-ca.crt"))
189188
require.NoError(t, err, "error reading CA file")

sdk/testing/server/fixture.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"strings"
3232
"sync"
3333
"syscall"
34-
"testing"
3534
"time"
3635

3736
"github.com/egymgmbh/go-prefix-writer/prefixer"
@@ -61,15 +60,15 @@ const kcpBinariesDirEnvDir = "KCP_BINARIES_DIR"
6160

6261
// RunInProcessFunc instantiates the kcp server in process for easier debugging.
6362
// It is here to decouple the rest of the code from kcp core dependencies.
64-
var RunInProcessFunc func(t *testing.T, dataDir string, args []string) (<-chan struct{}, error)
63+
var RunInProcessFunc func(t TestingT, dataDir string, args []string) (<-chan struct{}, error)
6564

6665
// Fixture manages the lifecycle of a set of kcp servers.
6766
//
6867
// Deprecated for use outside this package. Prefer PrivateKcpServer().
6968
type Fixture = map[string]RunningServer
7069

7170
// NewFixture returns a new kcp server fixture.
72-
func NewFixture(t *testing.T, cfgs ...Config) Fixture {
71+
func NewFixture(t TestingT, cfgs ...Config) Fixture {
7372
t.Helper()
7473

7574
// Initialize servers from the provided configuration
@@ -170,10 +169,10 @@ type kcpServer struct {
170169
cfg clientcmd.ClientConfig
171170
kubeconfigPath string
172171

173-
t *testing.T
172+
t TestingT
174173
}
175174

176-
func newKcpServer(t *testing.T, cfg Config, artifactDir, dataDir, clientCADir string) (*kcpServer, error) {
175+
func newKcpServer(t TestingT, cfg Config, artifactDir, dataDir, clientCADir string) (*kcpServer, error) {
177176
t.Helper()
178177

179178
kcpListenPort, err := GetFreePort(t)
@@ -462,12 +461,12 @@ func (c *kcpServer) config(context string) (*rest.Config, error) {
462461
return restConfig, nil
463462
}
464463

465-
func (c *kcpServer) ClientCAUserConfig(t *testing.T, config *rest.Config, name string, groups ...string) *rest.Config {
464+
func (c *kcpServer) ClientCAUserConfig(t TestingT, config *rest.Config, name string, groups ...string) *rest.Config {
466465
return clientCAUserConfig(t, config, c.clientCADir, name, groups...)
467466
}
468467

469468
// BaseConfig returns a rest.Config for the "base" context. Client-side throttling is disabled (QPS=-1).
470-
func (c *kcpServer) BaseConfig(t *testing.T) *rest.Config {
469+
func (c *kcpServer) BaseConfig(t TestingT) *rest.Config {
471470
t.Helper()
472471

473472
cfg, err := c.config("base")
@@ -477,7 +476,7 @@ func (c *kcpServer) BaseConfig(t *testing.T) *rest.Config {
477476
}
478477

479478
// RootShardSystemMasterBaseConfig returns a rest.Config for the "shard-base" context. Client-side throttling is disabled (QPS=-1).
480-
func (c *kcpServer) RootShardSystemMasterBaseConfig(t *testing.T) *rest.Config {
479+
func (c *kcpServer) RootShardSystemMasterBaseConfig(t TestingT) *rest.Config {
481480
t.Helper()
482481

483482
cfg, err := c.config("shard-base")
@@ -488,7 +487,7 @@ func (c *kcpServer) RootShardSystemMasterBaseConfig(t *testing.T) *rest.Config {
488487
}
489488

490489
// ShardSystemMasterBaseConfig returns a rest.Config for the "shard-base" context of a given shard. Client-side throttling is disabled (QPS=-1).
491-
func (c *kcpServer) ShardSystemMasterBaseConfig(t *testing.T, shard string) *rest.Config {
490+
func (c *kcpServer) ShardSystemMasterBaseConfig(t TestingT, shard string) *rest.Config {
492491
t.Helper()
493492

494493
if shard != corev1alpha1.RootShard {
@@ -545,14 +544,14 @@ func (c *kcpServer) CADirectory() string {
545544
return c.dataDir
546545
}
547546

548-
func (c *kcpServer) Artifact(t *testing.T, producer func() (runtime.Object, error)) {
547+
func (c *kcpServer) Artifact(t TestingT, producer func() (runtime.Object, error)) {
549548
t.Helper()
550549
artifact(t, c, producer)
551550
}
552551

553552
// artifact registers the data-producing function to run and dump the YAML-formatted output
554553
// to the artifact directory for the test before the kcp process is terminated.
555-
func artifact(t *testing.T, server RunningServer, producer func() (runtime.Object, error)) {
554+
func artifact(t TestingT, server RunningServer, producer func() (runtime.Object, error)) {
556555
t.Helper()
557556

558557
subDir := filepath.Join("artifacts", "kcp", server.Name())

sdk/testing/server/interface.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package server
1818

1919
import (
20-
"testing"
21-
2220
"k8s.io/apimachinery/pkg/runtime"
2321
"k8s.io/client-go/rest"
2422
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@@ -28,11 +26,11 @@ type RunningServer interface {
2826
Name() string
2927
KubeconfigPath() string
3028
RawConfig() (clientcmdapi.Config, error)
31-
BaseConfig(t *testing.T) *rest.Config
32-
RootShardSystemMasterBaseConfig(t *testing.T) *rest.Config
33-
ShardSystemMasterBaseConfig(t *testing.T, shard string) *rest.Config
29+
BaseConfig(t TestingT) *rest.Config
30+
RootShardSystemMasterBaseConfig(t TestingT) *rest.Config
31+
ShardSystemMasterBaseConfig(t TestingT, shard string) *rest.Config
3432
ShardNames() []string
35-
Artifact(t *testing.T, producer func() (runtime.Object, error))
36-
ClientCAUserConfig(t *testing.T, config *rest.Config, name string, groups ...string) *rest.Config
33+
Artifact(t TestingT, producer func() (runtime.Object, error))
34+
ClientCAUserConfig(t TestingT, config *rest.Config, name string, groups ...string) *rest.Config
3735
CADirectory() string
3836
}

sdk/testing/server/metrics.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"os"
2727
"path/filepath"
2828
"syscall"
29-
"testing"
3029
"time"
3130

3231
"github.com/stretchr/testify/require"
@@ -39,7 +38,7 @@ import (
3938
kcptestinghelpers "github.com/kcp-dev/kcp/sdk/testing/helpers"
4039
)
4140

42-
func gatherMetrics(ctx context.Context, t *testing.T, server RunningServer, directory string) {
41+
func gatherMetrics(ctx context.Context, t TestingT, server RunningServer, directory string) {
4342
cfg := server.RootShardSystemMasterBaseConfig(t)
4443
client, err := kcpclientset.NewForConfig(cfg)
4544
if err != nil {
@@ -61,7 +60,7 @@ func gatherMetrics(ctx context.Context, t *testing.T, server RunningServer, dire
6160
}
6261
}
6362

64-
func scrapeMetricsForServer(t *testing.T, srv RunningServer) {
63+
func scrapeMetricsForServer(t TestingT, srv RunningServer) {
6564
promUrl, set := os.LookupEnv("PROMETHEUS_URL")
6665
if !set || promUrl == "" {
6766
t.Logf("PROMETHEUS_URL environment variable unset, skipping Prometheus scrape config generation")

sdk/testing/server/port.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ import (
2323
"os"
2424
"path/filepath"
2525
"strconv"
26-
"testing"
2726
)
2827

2928
// GetFreePort asks the kernel for a free open port that is ready to use.
30-
func GetFreePort(t *testing.T) (string, error) {
29+
func GetFreePort(t TestingT) (string, error) {
3130
t.Helper()
3231

3332
for {

sdk/testing/server/ready.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"errors"
2222
"fmt"
2323
"strings"
24-
"testing"
2524
"time"
2625

2726
"k8s.io/apimachinery/pkg/util/sets"
@@ -68,7 +67,7 @@ func waitForEndpoint(ctx context.Context, client *rest.RESTClient, endpoint stri
6867
}
6968

7069
// MonitorEndpoints keeps watching the given endpoints and fails t on error.
71-
func MonitorEndpoints(t *testing.T, client *rest.Config, endpoints ...string) {
70+
func MonitorEndpoints(t TestingT, client *rest.Config, endpoints ...string) {
7271
ctx, cancel := context.WithCancel(context.Background())
7372
t.Cleanup(cancel)
7473
for _, endpoint := range endpoints {
@@ -78,7 +77,7 @@ func MonitorEndpoints(t *testing.T, client *rest.Config, endpoints ...string) {
7877
}
7978
}
8079

81-
func monitorEndpoint(ctx context.Context, t *testing.T, cfg *rest.Config, endpoint string) {
80+
func monitorEndpoint(ctx context.Context, t TestingT, cfg *rest.Config, endpoint string) {
8281
cfg = rest.CopyConfig(cfg)
8382
if cfg.NegotiatedSerializer == nil {
8483
cfg.NegotiatedSerializer = kubernetesscheme.Codecs.WithoutConversion()
@@ -89,13 +88,6 @@ func monitorEndpoint(ctx context.Context, t *testing.T, cfg *rest.Config, endpoi
8988
return
9089
}
9190

92-
// we need a shorter deadline than the server, or else:
93-
// timeout.go:135] post-timeout activity - time-elapsed: 23.784917ms, GET "/livez" result: Header called after Handler finished
94-
if deadline, ok := t.Deadline(); ok {
95-
deadlinedCtx, deadlinedCancel := context.WithDeadline(ctx, deadline.Add(-20*time.Second))
96-
ctx = deadlinedCtx
97-
t.Cleanup(deadlinedCancel) // this does not really matter but govet is upset
98-
}
9991
var errCount int
10092
errs := sets.New[string]()
10193
wait.UntilWithContext(ctx, func(ctx context.Context) {
@@ -108,7 +100,14 @@ func monitorEndpoint(ctx context.Context, t *testing.T, cfg *rest.Config, endpoi
108100
errCount++
109101
errs.Insert(fmt.Sprintf("failed components: %v", unreadyComponentsFromError(err)))
110102
if errCount == 2 {
111-
t.Errorf("error contacting %s: %v", endpoint, sets.List[string](errs))
103+
select {
104+
case <-ctx.Done():
105+
// ignore error if we're already shutting down
106+
default:
107+
if !t.Failed() {
108+
t.Errorf("error contacting %s: %v", endpoint, sets.List[string](errs))
109+
}
110+
}
112111
}
113112
}
114113
// otherwise, reset the counters

0 commit comments

Comments
 (0)