/* * * Copyright 2019 gRPC 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 test import ( "context" "testing" "time" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" testpb "google.golang.org/grpc/test/grpc_testing" ) func (s) TestContextCanceled(t *testing.T) { ss := &stubServer{ fullDuplexCall: func(stream testpb.TestService_FullDuplexCallServer) error { stream.SetTrailer(metadata.New(map[string]string{"a": "b"})) return status.Error(codes.PermissionDenied, "perm denied") }, } if err := ss.Start(nil); err != nil { t.Fatalf("Error starting endpoint server: %v", err) } defer ss.Stop() // Runs 10 rounds of tests with the given delay and returns counts of status codes. // Fails in case of trailer/status code inconsistency. const cntRetry uint = 10 runTest := func(delay time.Duration) (cntCanceled, cntPermDenied uint) { for i := uint(0); i < cntRetry; i++ { ctx, cancel := context.WithTimeout(context.Background(), delay) defer cancel() str, err := ss.client.FullDuplexCall(ctx) if err != nil { continue } _, err = str.Recv() if err == nil { t.Fatalf("non-nil error expected from Recv()") } _, trlOk := str.Trailer()["a"] switch status.Code(err) { case codes.PermissionDenied: if !trlOk { t.Fatalf(`status err: %v; wanted key "a" in trailer but didn't get it`, err) } cntPermDenied++ case codes.DeadlineExceeded: if trlOk { t.Fatalf(`status err: %v; didn't want key "a" in trailer but got it`, err) } cntCanceled++ default: t.Fatalf(`unexpected status err: %v`, err) } } return cntCanceled, cntPermDenied } // Tries to find the delay that causes canceled/perm denied race. canceledOk, permDeniedOk := false, false for lower, upper := time.Duration(0), 2*time.Millisecond; lower <= upper; { delay := lower + (upper-lower)/2 cntCanceled, cntPermDenied := runTest(delay) if cntPermDenied > 0 && cntCanceled > 0 { // Delay that causes the race is found. return } // Set OK flags. if cntCanceled > 0 { canceledOk = true } if cntPermDenied > 0 { permDeniedOk = true } if cntPermDenied == 0 { // No perm denied, increase the delay. lower += (upper-lower)/10 + 1 } else { // All perm denied, decrease the delay. upper -= (upper-lower)/10 + 1 } } if !canceledOk || !permDeniedOk { t.Fatalf(`couldn't find the delay that causes canceled/perm denied race.`) } }