You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

177 lines
4.0 KiB

  1. // Copyright 2016 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. package errgroup_test
  5. import (
  6. "context"
  7. "errors"
  8. "fmt"
  9. "net/http"
  10. "os"
  11. "testing"
  12. "golang.org/x/sync/errgroup"
  13. )
  14. var (
  15. Web = fakeSearch("web")
  16. Image = fakeSearch("image")
  17. Video = fakeSearch("video")
  18. )
  19. type Result string
  20. type Search func(ctx context.Context, query string) (Result, error)
  21. func fakeSearch(kind string) Search {
  22. return func(_ context.Context, query string) (Result, error) {
  23. return Result(fmt.Sprintf("%s result for %q", kind, query)), nil
  24. }
  25. }
  26. // JustErrors illustrates the use of a Group in place of a sync.WaitGroup to
  27. // simplify goroutine counting and error handling. This example is derived from
  28. // the sync.WaitGroup example at https://golang.org/pkg/sync/#example_WaitGroup.
  29. func ExampleGroup_justErrors() {
  30. var g errgroup.Group
  31. var urls = []string{
  32. "http://www.golang.org/",
  33. "http://www.google.com/",
  34. "http://www.somestupidname.com/",
  35. }
  36. for _, url := range urls {
  37. // Launch a goroutine to fetch the URL.
  38. url := url // https://golang.org/doc/faq#closures_and_goroutines
  39. g.Go(func() error {
  40. // Fetch the URL.
  41. resp, err := http.Get(url)
  42. if err == nil {
  43. resp.Body.Close()
  44. }
  45. return err
  46. })
  47. }
  48. // Wait for all HTTP fetches to complete.
  49. if err := g.Wait(); err == nil {
  50. fmt.Println("Successfully fetched all URLs.")
  51. }
  52. }
  53. // Parallel illustrates the use of a Group for synchronizing a simple parallel
  54. // task: the "Google Search 2.0" function from
  55. // https://talks.golang.org/2012/concurrency.slide#46, augmented with a Context
  56. // and error-handling.
  57. func ExampleGroup_parallel() {
  58. Google := func(ctx context.Context, query string) ([]Result, error) {
  59. g, ctx := errgroup.WithContext(ctx)
  60. searches := []Search{Web, Image, Video}
  61. results := make([]Result, len(searches))
  62. for i, search := range searches {
  63. i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines
  64. g.Go(func() error {
  65. result, err := search(ctx, query)
  66. if err == nil {
  67. results[i] = result
  68. }
  69. return err
  70. })
  71. }
  72. if err := g.Wait(); err != nil {
  73. return nil, err
  74. }
  75. return results, nil
  76. }
  77. results, err := Google(context.Background(), "golang")
  78. if err != nil {
  79. fmt.Fprintln(os.Stderr, err)
  80. return
  81. }
  82. for _, result := range results {
  83. fmt.Println(result)
  84. }
  85. // Output:
  86. // web result for "golang"
  87. // image result for "golang"
  88. // video result for "golang"
  89. }
  90. func TestZeroGroup(t *testing.T) {
  91. err1 := errors.New("errgroup_test: 1")
  92. err2 := errors.New("errgroup_test: 2")
  93. cases := []struct {
  94. errs []error
  95. }{
  96. {errs: []error{}},
  97. {errs: []error{nil}},
  98. {errs: []error{err1}},
  99. {errs: []error{err1, nil}},
  100. {errs: []error{err1, nil, err2}},
  101. }
  102. for _, tc := range cases {
  103. var g errgroup.Group
  104. var firstErr error
  105. for i, err := range tc.errs {
  106. err := err
  107. g.Go(func() error { return err })
  108. if firstErr == nil && err != nil {
  109. firstErr = err
  110. }
  111. if gErr := g.Wait(); gErr != firstErr {
  112. t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+
  113. "g.Wait() = %v; want %v",
  114. g, tc.errs[:i+1], err, firstErr)
  115. }
  116. }
  117. }
  118. }
  119. func TestWithContext(t *testing.T) {
  120. errDoom := errors.New("group_test: doomed")
  121. cases := []struct {
  122. errs []error
  123. want error
  124. }{
  125. {want: nil},
  126. {errs: []error{nil}, want: nil},
  127. {errs: []error{errDoom}, want: errDoom},
  128. {errs: []error{errDoom, nil}, want: errDoom},
  129. }
  130. for _, tc := range cases {
  131. g, ctx := errgroup.WithContext(context.Background())
  132. for _, err := range tc.errs {
  133. err := err
  134. g.Go(func() error { return err })
  135. }
  136. if err := g.Wait(); err != tc.want {
  137. t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+
  138. "g.Wait() = %v; want %v",
  139. g, tc.errs, err, tc.want)
  140. }
  141. canceled := false
  142. select {
  143. case <-ctx.Done():
  144. canceled = true
  145. default:
  146. }
  147. if !canceled {
  148. t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+
  149. "ctx.Done() was not closed",
  150. g, tc.errs)
  151. }
  152. }
  153. }