github.com/aaabigfish/gopkg@v1.1.0/syncx/errgroup/errgroup_test.go (about) 1 package errgroup 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math" 8 "net/http" 9 "os" 10 "testing" 11 "time" 12 ) 13 14 type ABC struct { 15 CBA int 16 } 17 18 func TestNormal(t *testing.T) { 19 var ( 20 abcs = make(map[int]*ABC) 21 g Group 22 err error 23 ) 24 for i := 0; i < 10; i++ { 25 abcs[i] = &ABC{CBA: i} 26 } 27 g.Go(func(context.Context) (err error) { 28 abcs[1].CBA++ 29 return 30 }) 31 g.Go(func(context.Context) (err error) { 32 abcs[2].CBA++ 33 return 34 }) 35 if err = g.Wait(); err != nil { 36 t.Log(err) 37 } 38 t.Log(abcs) 39 } 40 41 func sleep1s(context.Context) error { 42 time.Sleep(time.Second) 43 return nil 44 } 45 46 func TestGOMAXPROCS(t *testing.T) { 47 // 没有并发数限制 48 g := Group{} 49 now := time.Now() 50 g.Go(sleep1s) 51 g.Go(sleep1s) 52 g.Go(sleep1s) 53 g.Go(sleep1s) 54 g.Wait() 55 sec := math.Round(time.Since(now).Seconds()) 56 if sec != 1 { 57 t.FailNow() 58 } 59 // 限制并发数 60 g2 := Group{} 61 g2.GOMAXPROCS(2) 62 now = time.Now() 63 g2.Go(sleep1s) 64 g2.Go(sleep1s) 65 g2.Go(sleep1s) 66 g2.Go(sleep1s) 67 g2.Wait() 68 sec = math.Round(time.Since(now).Seconds()) 69 if sec != 2 { 70 t.FailNow() 71 } 72 // context canceled 73 var canceled bool 74 g3 := WithCancel(context.Background()) 75 g3.GOMAXPROCS(2) 76 g3.Go(func(context.Context) error { 77 return fmt.Errorf("error for testing errgroup context") 78 }) 79 g3.Go(func(ctx context.Context) error { 80 time.Sleep(time.Second) 81 select { 82 case <-ctx.Done(): 83 canceled = true 84 default: 85 } 86 return nil 87 }) 88 g3.Wait() 89 if !canceled { 90 t.FailNow() 91 } 92 } 93 94 func TestRecover(t *testing.T) { 95 var ( 96 abcs = make(map[int]*ABC) 97 g Group 98 err error 99 ) 100 g.Go(func(context.Context) (err error) { 101 abcs[1].CBA++ 102 return 103 }) 104 g.Go(func(context.Context) (err error) { 105 abcs[2].CBA++ 106 return 107 }) 108 if err = g.Wait(); err != nil { 109 t.Logf("error:%+v", err) 110 return 111 } 112 t.FailNow() 113 } 114 115 func TestRecover2(t *testing.T) { 116 var ( 117 g Group 118 err error 119 ) 120 g.Go(func(context.Context) (err error) { 121 panic("2233") 122 }) 123 if err = g.Wait(); err != nil { 124 t.Logf("error:%+v", err) 125 return 126 } 127 t.FailNow() 128 } 129 130 var ( 131 Web = fakeSearch("web") 132 Image = fakeSearch("image") 133 Video = fakeSearch("video") 134 ) 135 136 type Result string 137 type Search func(ctx context.Context, query string) (Result, error) 138 139 func fakeSearch(kind string) Search { 140 return func(_ context.Context, query string) (Result, error) { 141 return Result(fmt.Sprintf("%s result for %q", kind, query)), nil 142 } 143 } 144 145 // JustErrors illustrates the use of a Group in place of a sync.WaitGroup to 146 // simplify goroutine counting and error handling. This example is derived from 147 // the sync.WaitGroup example at https://golang.org/pkg/sync/#example_WaitGroup. 148 func ExampleGroup_justErrors() { 149 var g Group 150 var urls = []string{ 151 "http://www.golang.org/", 152 "http://www.google.com/", 153 "http://www.somestupidname.com/", 154 } 155 for _, url := range urls { 156 // Launch a goroutine to fetch the URL. 157 url := url // https://golang.org/doc/faq#closures_and_goroutines 158 g.Go(func(context.Context) error { 159 // Fetch the URL. 160 resp, err := http.Get(url) 161 if err == nil { 162 resp.Body.Close() 163 } 164 return err 165 }) 166 } 167 // Wait for all HTTP fetches to complete. 168 if err := g.Wait(); err == nil { 169 fmt.Println("Successfully fetched all URLs.") 170 } 171 } 172 173 // Parallel illustrates the use of a Group for synchronizing a simple parallel 174 // task: the "Google Search 2.0" function from 175 // https://talks.golang.org/2012/concurrency.slide#46, augmented with a Context 176 // and error-handling. 177 func ExampleGroup_parallel() { 178 Google := func(ctx context.Context, query string) ([]Result, error) { 179 g := WithContext(ctx) 180 181 searches := []Search{Web, Image, Video} 182 results := make([]Result, len(searches)) 183 for i, search := range searches { 184 i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines 185 g.Go(func(context.Context) error { 186 result, err := search(ctx, query) 187 if err == nil { 188 results[i] = result 189 } 190 return err 191 }) 192 } 193 if err := g.Wait(); err != nil { 194 return nil, err 195 } 196 return results, nil 197 } 198 199 results, err := Google(context.Background(), "golang") 200 if err != nil { 201 fmt.Fprintln(os.Stderr, err) 202 return 203 } 204 for _, result := range results { 205 fmt.Println(result) 206 } 207 208 // Output: 209 // web result for "golang" 210 // image result for "golang" 211 // video result for "golang" 212 } 213 214 func TestZeroGroup(t *testing.T) { 215 err1 := errors.New("errgroup_test: 1") 216 err2 := errors.New("errgroup_test: 2") 217 218 cases := []struct { 219 errs []error 220 }{ 221 {errs: []error{}}, 222 {errs: []error{nil}}, 223 {errs: []error{err1}}, 224 {errs: []error{err1, nil}}, 225 {errs: []error{err1, nil, err2}}, 226 } 227 228 for _, tc := range cases { 229 var g Group 230 231 var firstErr error 232 for i, err := range tc.errs { 233 err := err 234 g.Go(func(context.Context) error { return err }) 235 236 if firstErr == nil && err != nil { 237 firstErr = err 238 } 239 240 if gErr := g.Wait(); gErr != firstErr { 241 t.Errorf("after g.Go(func() error { return err }) for err in %v\n"+ 242 "g.Wait() = %v; want %v", tc.errs[:i+1], err, firstErr) 243 } 244 } 245 } 246 } 247 248 func TestWithCancel(t *testing.T) { 249 g := WithCancel(context.Background()) 250 g.Go(func(ctx context.Context) error { 251 time.Sleep(100 * time.Millisecond) 252 return fmt.Errorf("boom") 253 }) 254 var doneErr error 255 g.Go(func(ctx context.Context) error { 256 select { 257 case <-ctx.Done(): 258 doneErr = ctx.Err() 259 } 260 return doneErr 261 }) 262 g.Wait() 263 if doneErr != context.Canceled { 264 t.Error("error should be Canceled") 265 } 266 }