errgroup_test.go 5.5 KB

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