diff --git a/util/testenv/testenv.go b/util/testenv/testenv.go index 1ae1fe8a8..c6792bb4f 100644 --- a/util/testenv/testenv.go +++ b/util/testenv/testenv.go @@ -8,6 +8,7 @@ package testenv import ( "context" "flag" + "io" "tailscale.com/types/lazy" ) @@ -23,6 +24,8 @@ func InTest() bool { // TB is testing.TB, to avoid importing "testing" in non-test code. type TB interface { + ArtifactDir() string + Attr(key, value string) Cleanup(func()) Error(args ...any) Errorf(format string, args ...any) @@ -35,6 +38,7 @@ type TB interface { Log(args ...any) Logf(format string, args ...any) Name() string + Output() io.Writer Setenv(key, value string) Chdir(dir string) Skip(args ...any) diff --git a/util/testenv/testenv_test.go b/util/testenv/testenv_test.go index 3001d19eb..181f017f7 100644 --- a/util/testenv/testenv_test.go +++ b/util/testenv/testenv_test.go @@ -4,6 +4,7 @@ package testenv import ( + "reflect" "testing" "tailscale.com/tstest/deptest" @@ -29,3 +30,24 @@ func TestInParallelTestFalse(t *testing.T) { t.Fatal("InParallelTest should return false before t.Parallel has been called") } } + +// TestMatchesTestingTB verifies that TB has every exported method of +// testing.TB, with matching signatures. It can't be a compile-time +// assertion because testing.TB has an unexported method. +func TestMatchesTestingTB(t *testing.T) { + want := reflect.TypeFor[testing.TB]() + got := reflect.TypeFor[TB]() + for m := range want.Methods() { + if m.PkgPath != "" { + continue // unexported + } + gm, ok := got.MethodByName(m.Name) + if !ok { + t.Errorf("TB lacks method %s%v", m.Name, m.Type) + continue + } + if gm.Type != m.Type { + t.Errorf("TB method %s has type %v; want %v", m.Name, gm.Type, m.Type) + } + } +}