go.dedis.ch/onet/v4@v4.0.0-pre1/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, "graceful") ||
    33  			strings.Contains(stack, "sigqueue") ||
    34  			strings.Contains(stack, "log.MainTest") ||
    35  			matchesUserUninterestingGoroutine(stack) {
    36  			continue
    37  		}
    38  
    39  		gs = append(gs, stack)
    40  	}
    41  	sort.Strings(gs)
    42  	return
    43  }
    44  
    45  var userUninterestingGoroutines []string
    46  
    47  // AddUserUninterestingGoroutine can be called when the environment of some
    48  // specific tests leaks goroutines unknown to interestingGoroutines().
    49  // This function is not safe for concurrent execution. The caller should add
    50  // all of the desired exceptions before launching any goroutines.
    51  func AddUserUninterestingGoroutine(newGr string) {
    52  	userUninterestingGoroutines = append(userUninterestingGoroutines, newGr)
    53  }
    54  
    55  func matchesUserUninterestingGoroutine(stack string) bool {
    56  	for _, gr := range userUninterestingGoroutines {
    57  		if strings.Contains(stack, gr) {
    58  			return true
    59  		}
    60  	}
    61  
    62  	return false
    63  }
    64  
    65  // AfterTest can be called to wait for leaking goroutines to finish. If
    66  // they do not finish after a reasonable time (600ms) the test will fail.
    67  //
    68  // Inspired by https://golang.org/src/net/http/main_test.go
    69  // and https://github.com/coreos/etcd/blob/master/pkg/testutil/leak.go
    70  func AfterTest(t *testing.T) {
    71  	var stackCount map[string]int
    72  	for i := 0; i < 10; i++ {
    73  		n := 0
    74  		stackCount = make(map[string]int)
    75  		gs := interestingGoroutines()
    76  		for _, g := range gs {
    77  			stackCount[g]++
    78  			n++
    79  		}
    80  		if n == 0 {
    81  			break
    82  		}
    83  		// Wait for goroutines to schedule and die off:
    84  		time.Sleep(100 * time.Millisecond)
    85  	}
    86  	if len(stackCount) > 0 {
    87  		for stack, count := range stackCount {
    88  			if t != nil {
    89  				t.Logf("%d instances of:\n%s\n", count, stack)
    90  			} else {
    91  				Error(fmt.Sprintf("%d instances of:\n%s\n", count, stack))
    92  			}
    93  		}
    94  		Print("Stack-trace of caller: ", Stack())
    95  		if t != nil {
    96  			t.Fatalf("Test leaks %d goroutines.", len(stackCount))
    97  		} else {
    98  			Fatal(fmt.Sprintf("Test leaks %d goroutines.", len(stackCount)))
    99  		}
   100  	}
   101  }
   102  
   103  // Stack converts []byte to string
   104  func Stack() string {
   105  	return string(debug.Stack())
   106  }