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