Matt Rosencrantz | 75250d3 | 2015-08-19 08:13:05 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package goroutines |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "runtime" |
| 10 | "strings" |
| 11 | "sync" |
| 12 | "testing" |
| 13 | "time" |
| 14 | ) |
| 15 | |
| 16 | func wrappedWaitForIt(wg *sync.WaitGroup, wait chan struct{}, n int64) { |
| 17 | if n == 0 { |
| 18 | waitForIt(wg, wait) |
| 19 | } else { |
| 20 | wrappedWaitForIt(wg, wait, n-1) |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | func waitForIt(wg *sync.WaitGroup, wait chan struct{}) { |
| 25 | wg.Done() |
| 26 | <-wait |
| 27 | } |
| 28 | |
| 29 | func runGoA(wg *sync.WaitGroup, wait chan struct{}) { |
| 30 | go waitForIt(wg, wait) |
| 31 | } |
| 32 | |
| 33 | func runGoB(wg *sync.WaitGroup, wait chan struct{}) { |
| 34 | go wrappedWaitForIt(wg, wait, 3) |
| 35 | } |
| 36 | |
| 37 | func runGoC(wg *sync.WaitGroup, wait chan struct{}) { |
| 38 | go func() { |
| 39 | wg.Done() |
| 40 | <-wait |
| 41 | }() |
| 42 | } |
| 43 | |
| 44 | func TestGet(t *testing.T) { |
Matt Rosencrantz | 8ee3f4e | 2015-08-28 15:19:46 -0700 | [diff] [blame] | 45 | defer NoLeaks(t, 100*time.Millisecond)() |
Matt Rosencrantz | 75250d3 | 2015-08-19 08:13:05 -0700 | [diff] [blame] | 46 | var wg sync.WaitGroup |
| 47 | wg.Add(3) |
| 48 | wait := make(chan struct{}) |
| 49 | runGoA(&wg, wait) |
| 50 | runGoB(&wg, wait) |
| 51 | runGoC(&wg, wait) |
| 52 | wg.Wait() |
| 53 | gs, err := Get() |
| 54 | if err != nil { |
| 55 | t.Fatal(err) |
| 56 | } |
| 57 | close(wait) |
| 58 | |
| 59 | if len(gs) < 4 { |
| 60 | t.Errorf("Got %d goroutines, expected at least 4", len(gs)) |
| 61 | } |
| 62 | bycreator := map[string]*Goroutine{} |
| 63 | for _, g := range gs { |
| 64 | key := "" |
| 65 | if g.Creator != nil { |
| 66 | key = g.Creator.Call |
| 67 | } |
| 68 | bycreator[key] = g |
| 69 | } |
| 70 | a := bycreator["v.io/x/ref/test/goroutines.runGoA"] |
| 71 | if a == nil { |
| 72 | t.Errorf("runGoA is missing") |
Matt Rosencrantz | 00469a2 | 2015-09-02 10:43:27 -0700 | [diff] [blame] | 73 | } else if len(a.Stack) < 1 { |
| 74 | t.Errorf("got %d expected at least 1: %s", len(a.Stack), Format(a)) |
Matt Rosencrantz | 75250d3 | 2015-08-19 08:13:05 -0700 | [diff] [blame] | 75 | } else if !strings.HasPrefix(a.Stack[0].Call, "v.io/x/ref/test/goroutines.waitForIt") { |
| 76 | t.Errorf("got %s, wanted it to start with v.io/x/ref/test/goroutines.waitForIt", |
| 77 | a.Stack[0].Call) |
| 78 | } |
| 79 | b := bycreator["v.io/x/ref/test/goroutines.runGoB"] |
| 80 | if b == nil { |
| 81 | t.Errorf("runGoB is missing") |
Matt Rosencrantz | 00469a2 | 2015-09-02 10:43:27 -0700 | [diff] [blame] | 82 | } else if len(b.Stack) < 5 { |
| 83 | t.Errorf("got %d expected at least 5: %s", len(b.Stack), Format(b)) |
Matt Rosencrantz | 75250d3 | 2015-08-19 08:13:05 -0700 | [diff] [blame] | 84 | } |
| 85 | c := bycreator["v.io/x/ref/test/goroutines.runGoC"] |
| 86 | if c == nil { |
| 87 | t.Errorf("runGoC is missing") |
Matt Rosencrantz | 00469a2 | 2015-09-02 10:43:27 -0700 | [diff] [blame] | 88 | } else if len(c.Stack) < 1 { |
| 89 | t.Errorf("got %d expected at least 1: %s", len(c.Stack), Format(c)) |
Matt Rosencrantz | e7d55fb | 2015-08-27 13:00:57 -0700 | [diff] [blame] | 90 | } else if !strings.HasPrefix(c.Stack[0].Call, "v.io/x/ref/test/goroutines.") { |
| 91 | t.Errorf("got %s, wanted it to start with v.io/x/ref/test/goroutines.", |
Matt Rosencrantz | 75250d3 | 2015-08-19 08:13:05 -0700 | [diff] [blame] | 92 | c.Stack[0].Call) |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | func TestFormat(t *testing.T) { |
Matt Rosencrantz | 8ee3f4e | 2015-08-28 15:19:46 -0700 | [diff] [blame] | 97 | defer NoLeaks(t, 100*time.Millisecond)() |
| 98 | |
Matt Rosencrantz | 75250d3 | 2015-08-19 08:13:05 -0700 | [diff] [blame] | 99 | var wg sync.WaitGroup |
| 100 | wg.Add(3) |
| 101 | wait := make(chan struct{}) |
| 102 | runGoA(&wg, wait) |
| 103 | runGoB(&wg, wait) |
| 104 | runGoC(&wg, wait) |
| 105 | wg.Wait() |
| 106 | |
| 107 | buf := make([]byte, 1<<20) |
| 108 | buf = buf[:runtime.Stack(buf, true)] |
| 109 | close(wait) |
| 110 | |
| 111 | gs, err := Parse(buf) |
| 112 | if err != nil { |
| 113 | t.Fatal(err) |
| 114 | } |
| 115 | if formatted := Format(gs...); !bytes.Equal(buf, formatted) { |
| 116 | t.Errorf("got:\n%s\nwanted:\n%s\n", string(formatted), string(buf)) |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | type fakeErrorReporter struct { |
| 121 | calls int |
| 122 | extra int |
| 123 | formatted string |
| 124 | } |
| 125 | |
| 126 | func (f *fakeErrorReporter) Errorf(format string, args ...interface{}) { |
| 127 | f.calls++ |
| 128 | f.extra = args[0].(int) |
| 129 | f.formatted = args[1].(string) |
| 130 | } |
| 131 | |
| 132 | func TestNoLeaks(t *testing.T) { |
| 133 | er := &fakeErrorReporter{} |
| 134 | f := NoLeaks(er, 100*time.Millisecond) |
| 135 | |
| 136 | var wg sync.WaitGroup |
| 137 | wg.Add(3) |
| 138 | wait := make(chan struct{}) |
| 139 | runGoA(&wg, wait) |
| 140 | runGoB(&wg, wait) |
| 141 | runGoC(&wg, wait) |
| 142 | wg.Wait() |
| 143 | |
| 144 | f() |
| 145 | if er.calls != 1 { |
| 146 | t.Errorf("got %d, wanted 1: %s", er.calls, er.formatted) |
| 147 | } |
| 148 | if er.extra != 3 { |
| 149 | t.Errorf("got %d, wanted 3: %s", er.extra, er.formatted) |
| 150 | } |
| 151 | close(wait) |
| 152 | |
| 153 | *er = fakeErrorReporter{} |
| 154 | f() |
| 155 | if er.calls != 0 { |
| 156 | t.Errorf("got %d, wanted 0: %s", er.calls, er.formatted) |
| 157 | } |
| 158 | if er.extra != 0 { |
| 159 | t.Errorf("got %d, wanted 0: %s", er.extra, er.formatted) |
| 160 | } |
| 161 | } |