Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grpctest: Parent testing.T used for logging when running subtests with t.Run #7968

Open
arjan-bal opened this issue Dec 25, 2024 · 0 comments
Labels
Area: Testing Includes tests and testing utilities that we have for unit and e2e tests within our repo. Type: Testing

Comments

@arjan-bal
Copy link
Contributor

What version of gRPC are you using?

1.69

What version of Go are you using (go version)?

N/A

What operating system (Linux, Windows, …) and version?

N/A

What did you do?

Run a test that follows table driven style, similar to TestUnmarshalListener_WithUpdateValidatorFunc which has multiple subtests run using t.Run().

func (s) TestUnmarshalListener_WithUpdateValidatorFunc(t *testing.T) {

What did you expect to see?

In the test logs, ordering of events should be as follows:

  1. Name of parent test
  2. Setup logs
  3. Name of subtest 1
  4. Subtest 1 logs
  5. Name of subtest 2
  6. Subtest 2 logs

Example:

$ go test ./test/xds/ -failfast -count 1 -v -run "Test/UnmarshalListener_WithUpdateValidatorFunc/both_identity_and_root_providers_are_
not_present_in_bootstrap"
=== RUN   Test
=== RUN   Test/UnmarshalListener_WithUpdateValidatorFunc
=== RUN   Test/UnmarshalListener_WithUpdateValidatorFunc/both_identity_and_root_providers_are_not_present_in_bootstrap
    setup.go:45: Created new snapshot cache...
    tlogger.go:116: INFO server.go:685 [core] [Server #1]Server created  (t=+651.435µs)
    setup.go:45: Registered Aggregated Discovery Service (ADS)...
    setup.go:45: xDS management server serving at: 127.0.0.1:46695...
    tlogger.go:116: INFO server.go:881 [core] [Server #1 ListenSocket #2]ListenSocket created  (t=+960.269µs)
    tlogger.go:116: INFO server.go:685 [core] [Server #3]Server created  (t=+1.292673ms)

What did you see instead?

The parent test name is interleaved in subtest logs.

=== RUN   Test
=== RUN   Test/UnmarshalListener_WithUpdateValidatorFunc
=== RUN   Test/UnmarshalListener_WithUpdateValidatorFunc/both_identity_and_root_providers_are_not_present_in_bootstrap
    setup.go:45: Created new snapshot cache...
=== NAME  Test/UnmarshalListener_WithUpdateValidatorFunc
    tlogger.go:116: INFO server.go:685 [core] [Server #1]Server created  (t=+1.162218ms)
=== NAME  Test/UnmarshalListener_WithUpdateValidatorFunc/both_identity_and_root_providers_are_not_present_in_bootstrap
    setup.go:45: Registered Aggregated Discovery Service (ADS)...
    setup.go:45: xDS management server serving at: 127.0.0.1:38723...
=== NAME  Test/UnmarshalListener_WithUpdateValidatorFunc
    tlogger.go:116: INFO server.go:881 [core] [Server #1 ListenSocket #2]ListenSocket created  (t=+1.458969ms)
    tlogger.go:116: INFO server.go:685 [core] [Server #3]Server created  (t=+1.846345ms)
    tlogger.go:116: INFO client_refcounted.go:76 [xds] [xds-client 0xc00036e460] Created client with name "#server" and bootstrap configuration: (t=+2.014449ms)  
    tlogger.go:116: INFO client_refcounted.go:81 [xds] xDS node ID: 030e1a26-0e7c-4935-968d-0a948b03cd0f  (t=+2.045277ms)
    tlogger.go:116: INFO server.go:123 [xds] [xds-server 0xc00022da40] Created xds.GRPCServer  (t=+2.056109ms)
=== NAME  Test/UnmarshalListener_WithUpdateValidatorFunc/both_identity_and_root_providers_are_not_present_in_bootstrap
    stubserver.go:288: Started test service backend at "127.0.0.1:33307"
=== NAME  Test/UnmarshalListener_WithUpdateValidatorFunc

Root Cause

This issue is seen because the parent function's *testing.T is used to log for all substests

func RunSubTests(t *testing.T, x Interface) {
xt := reflect.TypeOf(x)
xv := reflect.ValueOf(x)
for i := 0; i < xt.NumMethod(); i++ {
methodName := xt.Method(i).Name
if !strings.HasPrefix(methodName, "Test") {
continue
}
tfunc := getTestFunc(t, xv, methodName)
t.Run(strings.TrimPrefix(methodName, "Test"), func(t *testing.T) {
// Run leakcheck in t.Cleanup() to guarantee it is run even if tfunc
// or setup uses t.Fatal().
//
// Note that a defer would run before t.Cleanup, so if a goroutine
// is closed by a test's t.Cleanup, a deferred leakcheck would fail.
t.Cleanup(func() { x.Teardown(t) })
x.Setup(t)
tfunc(t)
})
}
}

// Setup updates the tlogger.
func (Tester) Setup(t *testing.T) {
TLogger.Update(t)
// TODO: There is one final leak around closing connections without completely
// draining the recvBuffer that has yet to be resolved. All other leaks have been
// completely addressed, and this can be turned back on as soon as this issue is
// fixed.
leakcheck.SetTrackingBufferPool(logger{t: t})
}

A manual fix would be to call grpctest.Tester.Setup(t) at the beginning of each subtest. I tried finding a way of giving a setup hook to testing.T (similar to the t.Cleanup functions that run after tests), but there isn't a public API to do this. Since testing.T is a struct and not an interface, wrapping it to intercept call to t.Run isn't an option too.

@arjan-bal arjan-bal added Type: Bug Type: Testing Area: Testing Includes tests and testing utilities that we have for unit and e2e tests within our repo. and removed Type: Bug labels Dec 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Testing Includes tests and testing utilities that we have for unit and e2e tests within our repo. Type: Testing
Projects
None yet
Development

No branches or pull requests

1 participant