google.golang.org/grpc@v1.74.2/internal/leakcheck/leakcheck_test.go (about)

     1  /*
     2   *
     3   * Copyright 2017 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package leakcheck
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"google.golang.org/grpc/internal"
    30  )
    31  
    32  type testLogger struct {
    33  	errorCount int
    34  	errors     []string
    35  }
    36  
    37  func (e *testLogger) Logf(string, ...any) {
    38  }
    39  
    40  func (e *testLogger) Errorf(format string, args ...any) {
    41  	e.errors = append(e.errors, fmt.Sprintf(format, args...))
    42  	e.errorCount++
    43  }
    44  
    45  func TestCheck(t *testing.T) {
    46  	const leakCount = 3
    47  	ch := make(chan struct{})
    48  	for i := 0; i < leakCount; i++ {
    49  		go func() { <-ch }()
    50  	}
    51  	if leaked := interestingGoroutines(); len(leaked) != leakCount {
    52  		t.Errorf("interestingGoroutines() = %d, want length %d", len(leaked), leakCount)
    53  	}
    54  	e := &testLogger{}
    55  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    56  	defer cancel()
    57  	if CheckGoroutines(ctx, e); e.errorCount < leakCount {
    58  		t.Errorf("CheckGoroutines() = %d, want count %d", e.errorCount, leakCount)
    59  		t.Logf("leaked goroutines:\n%v", strings.Join(e.errors, "\n"))
    60  	}
    61  	close(ch)
    62  	ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second)
    63  	defer cancel()
    64  	CheckGoroutines(ctx, t)
    65  }
    66  
    67  func ignoredTestingLeak(d time.Duration) {
    68  	time.Sleep(d)
    69  }
    70  
    71  func TestCheckRegisterIgnore(t *testing.T) {
    72  	RegisterIgnoreGoroutine("ignoredTestingLeak")
    73  	go ignoredTestingLeak(3 * time.Second)
    74  	const leakCount = 3
    75  	ch := make(chan struct{})
    76  	for i := 0; i < leakCount; i++ {
    77  		go func() { <-ch }()
    78  	}
    79  	if leaked := interestingGoroutines(); len(leaked) != leakCount {
    80  		t.Errorf("interestingGoroutines() = %d, want length %d", len(leaked), leakCount)
    81  	}
    82  	e := &testLogger{}
    83  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    84  	defer cancel()
    85  	if CheckGoroutines(ctx, e); e.errorCount < leakCount {
    86  		t.Errorf("CheckGoroutines() = %d, want count %d", e.errorCount, leakCount)
    87  		t.Logf("leaked goroutines:\n%v", strings.Join(e.errors, "\n"))
    88  	}
    89  	close(ch)
    90  	ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second)
    91  	defer cancel()
    92  	CheckGoroutines(ctx, t)
    93  }
    94  
    95  // TestTrackTimers verifies that only leaked timers are reported and expired,
    96  // stopped timers are ignored.
    97  func TestTrackTimers(t *testing.T) {
    98  	TrackTimers()
    99  	const leakCount = 3
   100  	for i := 0; i < leakCount; i++ {
   101  		internal.TimeAfterFunc(2*time.Second, func() {
   102  			t.Logf("Timer %d fired.", i)
   103  		})
   104  	}
   105  	wg := sync.WaitGroup{}
   106  	// Let a couple of timers expire.
   107  	for i := 0; i < 2; i++ {
   108  		wg.Add(1)
   109  		internal.TimeAfterFunc(time.Millisecond, func() {
   110  			wg.Done()
   111  		})
   112  	}
   113  	wg.Wait()
   114  
   115  	// Stop a couple of timers.
   116  	for i := 0; i < leakCount; i++ {
   117  		t := internal.TimeAfterFunc(time.Hour, func() {
   118  			t.Error("Timer fired before test ended.")
   119  		})
   120  		t.Stop()
   121  	}
   122  
   123  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   124  	defer cancel()
   125  	e := &testLogger{}
   126  	CheckTimers(ctx, e)
   127  	if e.errorCount != leakCount {
   128  		t.Errorf("CheckTimers found %v leaks, want %v leaks", e.errorCount, leakCount)
   129  		t.Logf("leaked timers:\n%v", strings.Join(e.errors, "\n"))
   130  	}
   131  	ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second)
   132  	defer cancel()
   133  	CheckTimers(ctx, t)
   134  }