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  }