go.dedis.ch/onet/v3@v3.2.11-0.20210930124529-e36530bca7ef/log/testutil.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"runtime/debug"
     7  	"sort"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  func interestingGoroutines() (gs []string) {
    14  	buf := make([]byte, 2<<20)
    15  	buf = buf[:runtime.Stack(buf, true)]
    16  	for _, g := range strings.Split(string(buf), "\n\n") {
    17  		sl := strings.SplitN(g, "\n", 2)
    18  		if len(sl) != 2 {
    19  			continue
    20  		}
    21  		stack := strings.TrimSpace(sl[1])
    22  		if stack == "" ||
    23  			strings.Contains(stack, "(*LocalTest).CloseAll") ||
    24  			strings.Contains(stack, "created by testing.RunTests") ||
    25  			strings.Contains(stack, "testing.RunTests(") ||
    26  			strings.Contains(stack, "testing.(*T).Run(") ||
    27  			strings.Contains(stack, "testing.Main(") ||
    28  			strings.Contains(stack, "runtime.goexit") ||
    29  			strings.Contains(stack, "interestingGoroutines") ||
    30  			strings.Contains(stack, "created by runtime.gc") ||
    31  			strings.Contains(stack, "runtime.MHeap_Scavenger") ||
    32  			strings.Contains(stack, "sigqueue") ||
    33  			strings.Contains(stack, "log.MainTest") ||
    34  			matchesUserUninterestingGoroutine(stack) {
    35  			continue
    36  		}
    37  
    38  		gs = append(gs, stack)
    39  	}
    40  	sort.Strings(gs)
    41  	return
    42  }
    43  
    44  var userUninterestingGoroutines []string
    45  
    46  // AddUserUninterestingGoroutine can be called when the environment of some
    47  // specific tests leaks goroutines unknown to interestingGoroutines().
    48  // This function is not safe for concurrent execution. The caller should add
    49  // all of the desired exceptions before launching any goroutines.
    50  func AddUserUninterestingGoroutine(newGr string) {
    51  	userUninterestingGoroutines = append(userUninterestingGoroutines, newGr)
    52  }
    53  
    54  func matchesUserUninterestingGoroutine(stack string) bool {
    55  	for _, gr := range userUninterestingGoroutines {
    56  		if strings.Contains(stack, gr) {
    57  			return true
    58  		}
    59  	}
    60  
    61  	return false
    62  }
    63  
    64  // AfterTest can be called to wait for leaking goroutines to finish. If
    65  // they do not finish after a reasonable time (600ms) the test will fail.
    66  //
    67  // Inspired by https://golang.org/src/net/http/main_test.go
    68  // and https://github.com/coreos/etcd/blob/master/pkg/testutil/leak.go
    69  func AfterTest(t *testing.T) {
    70  	var stackCount map[string]int
    71  	for i := 0; i < 10; i++ {
    72  		n := 0
    73  		stackCount = make(map[string]int)
    74  		gs := interestingGoroutines()
    75  		for _, g := range gs {
    76  			stackCount[g]++
    77  			n++
    78  		}
    79  		if n == 0 {
    80  			break
    81  		}
    82  		// Wait for goroutines to schedule and die off:
    83  		time.Sleep(100 * time.Millisecond)
    84  	}
    85  	if len(stackCount) > 0 {
    86  		for stack, count := range stackCount {
    87  			if t != nil {
    88  				t.Logf("%d instances of:\n%s\n", count, stack)
    89  			} else {
    90  				Error(fmt.Sprintf("%d instances of:\n%s\n", count, stack))
    91  			}
    92  		}
    93  		Print("Stack-trace of caller: ", Stack())
    94  		if t != nil {
    95  			t.Fatalf("Test leaks %d goroutines.", len(stackCount))
    96  		} else {
    97  			Fatal(fmt.Sprintf("Test leaks %d goroutines.", len(stackCount)))
    98  		}
    99  	}
   100  }
   101  
   102  // Stack converts []byte to string
   103  func Stack() string {
   104  	return string(debug.Stack())
   105  }