Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / sync@v0.0.0-20200625203802-6e8e738ad208 / singleflight / singleflight_test.go
1 // Copyright 2013 The Go 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 singleflight
6
7 import (
8         "errors"
9         "fmt"
10         "sync"
11         "sync/atomic"
12         "testing"
13         "time"
14 )
15
16 func TestDo(t *testing.T) {
17         var g Group
18         v, err, _ := g.Do("key", func() (interface{}, error) {
19                 return "bar", nil
20         })
21         if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
22                 t.Errorf("Do = %v; want %v", got, want)
23         }
24         if err != nil {
25                 t.Errorf("Do error = %v", err)
26         }
27 }
28
29 func TestDoErr(t *testing.T) {
30         var g Group
31         someErr := errors.New("Some error")
32         v, err, _ := g.Do("key", func() (interface{}, error) {
33                 return nil, someErr
34         })
35         if err != someErr {
36                 t.Errorf("Do error = %v; want someErr %v", err, someErr)
37         }
38         if v != nil {
39                 t.Errorf("unexpected non-nil value %#v", v)
40         }
41 }
42
43 func TestDoDupSuppress(t *testing.T) {
44         var g Group
45         var wg1, wg2 sync.WaitGroup
46         c := make(chan string, 1)
47         var calls int32
48         fn := func() (interface{}, error) {
49                 if atomic.AddInt32(&calls, 1) == 1 {
50                         // First invocation.
51                         wg1.Done()
52                 }
53                 v := <-c
54                 c <- v // pump; make available for any future calls
55
56                 time.Sleep(10 * time.Millisecond) // let more goroutines enter Do
57
58                 return v, nil
59         }
60
61         const n = 10
62         wg1.Add(1)
63         for i := 0; i < n; i++ {
64                 wg1.Add(1)
65                 wg2.Add(1)
66                 go func() {
67                         defer wg2.Done()
68                         wg1.Done()
69                         v, err, _ := g.Do("key", fn)
70                         if err != nil {
71                                 t.Errorf("Do error: %v", err)
72                                 return
73                         }
74                         if s, _ := v.(string); s != "bar" {
75                                 t.Errorf("Do = %T %v; want %q", v, v, "bar")
76                         }
77                 }()
78         }
79         wg1.Wait()
80         // At least one goroutine is in fn now and all of them have at
81         // least reached the line before the Do.
82         c <- "bar"
83         wg2.Wait()
84         if got := atomic.LoadInt32(&calls); got <= 0 || got >= n {
85                 t.Errorf("number of calls = %d; want over 0 and less than %d", got, n)
86         }
87 }
88
89 // Test that singleflight behaves correctly after Forget called.
90 // See https://github.com/golang/go/issues/31420
91 func TestForget(t *testing.T) {
92         var g Group
93
94         var firstStarted, firstFinished sync.WaitGroup
95
96         firstStarted.Add(1)
97         firstFinished.Add(1)
98
99         firstCh := make(chan struct{})
100         go func() {
101                 g.Do("key", func() (i interface{}, e error) {
102                         firstStarted.Done()
103                         <-firstCh
104                         firstFinished.Done()
105                         return
106                 })
107         }()
108
109         firstStarted.Wait()
110         g.Forget("key") // from this point no two function using same key should be executed concurrently
111
112         var secondStarted int32
113         var secondFinished int32
114         var thirdStarted int32
115
116         secondCh := make(chan struct{})
117         secondRunning := make(chan struct{})
118         go func() {
119                 g.Do("key", func() (i interface{}, e error) {
120                         defer func() {
121                         }()
122                         atomic.AddInt32(&secondStarted, 1)
123                         // Notify that we started
124                         secondCh <- struct{}{}
125                         // Wait other get above signal
126                         <-secondRunning
127                         <-secondCh
128                         atomic.AddInt32(&secondFinished, 1)
129                         return 2, nil
130                 })
131         }()
132
133         close(firstCh)
134         firstFinished.Wait() // wait for first execution (which should not affect execution after Forget)
135
136         <-secondCh
137         // Notify second that we got the signal that it started
138         secondRunning <- struct{}{}
139         if atomic.LoadInt32(&secondStarted) != 1 {
140                 t.Fatal("Second execution should be executed due to usage of forget")
141         }
142
143         if atomic.LoadInt32(&secondFinished) == 1 {
144                 t.Fatal("Second execution should be still active")
145         }
146
147         close(secondCh)
148         result, _, _ := g.Do("key", func() (i interface{}, e error) {
149                 atomic.AddInt32(&thirdStarted, 1)
150                 return 3, nil
151         })
152
153         if atomic.LoadInt32(&thirdStarted) != 0 {
154                 t.Error("Third call should not be started because was started during second execution")
155         }
156         if result != 2 {
157                 t.Errorf("We should receive result produced by second call, expected: 2, got %d", result)
158         }
159 }