github.com/aavshr/aws-sdk-go@v1.41.3/internal/sync/singleflight/singleflight_test.go (about)

     1  // Copyright 2013 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  
     5  package singleflight
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"sync"
    11  	"sync/atomic"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  func TestDo(t *testing.T) {
    17  	t.Skip("singleflight tests not stable")
    18  	var g Group
    19  	v, err, _ := g.Do("key", func() (interface{}, error) {
    20  		return "bar", nil
    21  	})
    22  	if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
    23  		t.Errorf("Do = %v; want %v", got, want)
    24  	}
    25  	if err != nil {
    26  		t.Errorf("Do error = %v", err)
    27  	}
    28  }
    29  
    30  func TestDoErr(t *testing.T) {
    31  	t.Skip("singleflight tests not stable")
    32  	var g Group
    33  	someErr := errors.New("Some error")
    34  	v, err, _ := g.Do("key", func() (interface{}, error) {
    35  		return nil, someErr
    36  	})
    37  	if err != someErr {
    38  		t.Errorf("Do error = %v; want someErr %v", err, someErr)
    39  	}
    40  	if v != nil {
    41  		t.Errorf("unexpected non-nil value %#v", v)
    42  	}
    43  }
    44  
    45  func TestDoDupSuppress(t *testing.T) {
    46  	t.Skip("singleflight tests not stable")
    47  	var g Group
    48  	var wg1, wg2 sync.WaitGroup
    49  	c := make(chan string, 1)
    50  	var calls int32
    51  	fn := func() (interface{}, error) {
    52  		if atomic.AddInt32(&calls, 1) == 1 {
    53  			// First invocation.
    54  			wg1.Done()
    55  		}
    56  		v := <-c
    57  		c <- v // pump; make available for any future calls
    58  
    59  		time.Sleep(10 * time.Millisecond) // let more goroutines enter Do
    60  
    61  		return v, nil
    62  	}
    63  
    64  	const n = 10
    65  	wg1.Add(1)
    66  	for i := 0; i < n; i++ {
    67  		wg1.Add(1)
    68  		wg2.Add(1)
    69  		go func() {
    70  			defer wg2.Done()
    71  			wg1.Done()
    72  			v, err, _ := g.Do("key", fn)
    73  			if err != nil {
    74  				t.Errorf("Do error: %v", err)
    75  				return
    76  			}
    77  			if s, _ := v.(string); s != "bar" {
    78  				t.Errorf("Do = %T %v; want %q", v, v, "bar")
    79  			}
    80  		}()
    81  	}
    82  	wg1.Wait()
    83  	// At least one goroutine is in fn now and all of them have at
    84  	// least reached the line before the Do.
    85  	c <- "bar"
    86  	wg2.Wait()
    87  	if got := atomic.LoadInt32(&calls); got <= 0 || got >= n {
    88  		t.Errorf("number of calls = %d; want over 0 and less than %d", got, n)
    89  	}
    90  }
    91  
    92  // Test that singleflight behaves correctly after Forget called.
    93  // See https://github.com/golang/go/issues/31420
    94  func TestForget(t *testing.T) {
    95  	t.Skip("singleflight tests not stable")
    96  	var g Group
    97  
    98  	var firstStarted, firstFinished sync.WaitGroup
    99  
   100  	firstStarted.Add(1)
   101  	firstFinished.Add(1)
   102  
   103  	firstCh := make(chan struct{})
   104  	go func() {
   105  		g.Do("key", func() (i interface{}, e error) {
   106  			firstStarted.Done()
   107  			<-firstCh
   108  			firstFinished.Done()
   109  			return
   110  		})
   111  	}()
   112  
   113  	firstStarted.Wait()
   114  	g.Forget("key") // from this point no two function using same key should be executed concurrently
   115  
   116  	var secondStarted int32
   117  	var secondFinished int32
   118  	var thirdStarted int32
   119  
   120  	secondCh := make(chan struct{})
   121  	secondRunning := make(chan struct{})
   122  	go func() {
   123  		g.Do("key", func() (i interface{}, e error) {
   124  			defer func() {
   125  			}()
   126  			atomic.AddInt32(&secondStarted, 1)
   127  			// Notify that we started
   128  			secondCh <- struct{}{}
   129  			// Wait other get above signal
   130  			<-secondRunning
   131  			<-secondCh
   132  			atomic.AddInt32(&secondFinished, 1)
   133  			return 2, nil
   134  		})
   135  	}()
   136  
   137  	close(firstCh)
   138  	firstFinished.Wait() // wait for first execution (which should not affect execution after Forget)
   139  
   140  	<-secondCh
   141  	// Notify second that we got the signal that it started
   142  	secondRunning <- struct{}{}
   143  	if atomic.LoadInt32(&secondStarted) != 1 {
   144  		t.Fatal("Second execution should be executed due to usage of forget")
   145  	}
   146  
   147  	if atomic.LoadInt32(&secondFinished) == 1 {
   148  		t.Fatal("Second execution should be still active")
   149  	}
   150  
   151  	close(secondCh)
   152  	result, _, _ := g.Do("key", func() (i interface{}, e error) {
   153  		atomic.AddInt32(&thirdStarted, 1)
   154  		return 3, nil
   155  	})
   156  
   157  	if atomic.LoadInt32(&thirdStarted) != 0 {
   158  		t.Error("Third call should not be started because was started during second execution")
   159  	}
   160  	if result != 2 {
   161  		t.Errorf("We should receive result produced by second call, expected: 2, got %d", result)
   162  	}
   163  }