github.com/erda-project/erda-infra@v1.0.10-0.20240327085753-f3a249292aeb/pkg/parallel/group_test.go (about)

     1  // Copyright (c) 2021 Terminus, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package parallel
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"net/http"
    22  	"os"
    23  	"testing"
    24  )
    25  
    26  var (
    27  	Web   = fakeSearch("web")
    28  	Image = fakeSearch("image")
    29  	Video = fakeSearch("video")
    30  )
    31  
    32  type Result string
    33  type Search func(ctx context.Context, query string) (Result, error)
    34  
    35  func fakeSearch(kind string) Search {
    36  	return func(_ context.Context, query string) (Result, error) {
    37  		return Result(fmt.Sprintf("%s result for %q", kind, query)), nil
    38  	}
    39  }
    40  
    41  // JustErrors illustrates the use of a Group in place of a sync.WaitGroup to
    42  // simplify goroutine counting and error handling. This example is derived from
    43  // the sync.WaitGroup example at https://golang.org/pkg/sync/#example_WaitGroup.
    44  func ExampleGroup_justErrors() {
    45  	g := GoGroup(context.Background())
    46  	var urls = []string{
    47  		"http://www.golang.org/",
    48  		"http://www.google.com/",
    49  		"http://www.somestupidname.com/",
    50  	}
    51  	for _, url := range urls {
    52  		// Launch a goroutine to fetch the URL.
    53  		url := url // https://golang.org/doc/faq#closures_and_goroutines
    54  		g.Go(func(ctx context.Context) error {
    55  			// Fetch the URL.
    56  			resp, err := http.Get(url)
    57  			if err == nil {
    58  				resp.Body.Close()
    59  			}
    60  			return err
    61  		})
    62  	}
    63  	// Wait for all HTTP fetches to complete.
    64  	if err := g.Wait(); err == nil {
    65  		fmt.Println("Successfully fetched all URLs.")
    66  	}
    67  }
    68  
    69  // Parallel illustrates the use of a Group for synchronizing a simple parallel
    70  // task: the "Google Search 2.0" function from
    71  // https://talks.golang.org/2012/concurrency.slide#46, augmented with a Context
    72  // and error-handling.
    73  func ExampleGroup_parallel() {
    74  	Google := func(ctx context.Context, query string) ([]Result, error) {
    75  
    76  		g := GoGroup(context.Background())
    77  
    78  		searches := []Search{Web, Image, Video}
    79  		results := make([]Result, len(searches))
    80  		for i, search := range searches {
    81  			i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines
    82  			g.Go(func(ctx context.Context) error {
    83  				result, err := search(ctx, query)
    84  				if err == nil {
    85  					results[i] = result
    86  				}
    87  				return err
    88  			})
    89  		}
    90  		if err := g.Wait(); err != nil {
    91  			return nil, err
    92  		}
    93  		return results, nil
    94  	}
    95  
    96  	results, err := Google(context.Background(), "golang")
    97  	if err != nil {
    98  		fmt.Fprintln(os.Stderr, err)
    99  		return
   100  	}
   101  	for _, result := range results {
   102  		fmt.Println(result)
   103  	}
   104  
   105  	// Output:
   106  	// web result for "golang"
   107  	// image result for "golang"
   108  	// video result for "golang"
   109  }
   110  
   111  func TestZeroGroup(t *testing.T) {
   112  	err1 := errors.New("errgroup_test: 1")
   113  	err2 := errors.New("errgroup_test: 2")
   114  
   115  	cases := []struct {
   116  		errs []error
   117  	}{
   118  		{errs: []error{}},
   119  		{errs: []error{nil}},
   120  		{errs: []error{err1}},
   121  		{errs: []error{err1, nil}},
   122  		{errs: []error{err1, nil, err2}},
   123  	}
   124  
   125  	for _, tc := range cases {
   126  		g := GoGroup(context.Background())
   127  
   128  		var firstErr error
   129  		for i, err := range tc.errs {
   130  			err := err
   131  			g.Go(func(context.Context) error { return err })
   132  
   133  			if firstErr == nil && err != nil {
   134  				firstErr = err
   135  			}
   136  
   137  			if gErr := g.Wait(); gErr != firstErr {
   138  				t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+
   139  					"g.Wait() = %v; want %v",
   140  					g, tc.errs[:i+1], err, firstErr)
   141  			}
   142  		}
   143  	}
   144  }
   145  
   146  func TestWithContext(t *testing.T) {
   147  	errDoom := errors.New("group_test: doomed")
   148  
   149  	cases := []struct {
   150  		errs []error
   151  		want error
   152  	}{
   153  		{want: nil},
   154  		{errs: []error{nil}, want: nil},
   155  		{errs: []error{errDoom}, want: errDoom},
   156  		{errs: []error{errDoom, nil}, want: errDoom},
   157  	}
   158  
   159  	for _, tc := range cases {
   160  		g := GoGroup(context.Background())
   161  
   162  		for _, err := range tc.errs {
   163  			err := err
   164  			g.Go(func(context.Context) error { return err })
   165  		}
   166  
   167  		if err := g.Wait(); err != tc.want {
   168  			t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+
   169  				"g.Wait() = %v; want %v",
   170  				g, tc.errs, err, tc.want)
   171  		}
   172  
   173  		canceled := false
   174  		select {
   175  		case <-g.ctx.Done():
   176  			canceled = true
   177  		default:
   178  		}
   179  		if !canceled {
   180  			t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+
   181  				"ctx.Done() was not closed",
   182  				g, tc.errs)
   183  		}
   184  	}
   185  }