|  | // Copyright 2013 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package singleflight | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "fmt" | 
|  | "sync" | 
|  | "sync/atomic" | 
|  | "testing" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | func TestDo(t *testing.T) { | 
|  | var g Group | 
|  | v, err, _ := g.Do("key", func() (any, error) { | 
|  | return "bar", nil | 
|  | }) | 
|  | if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want { | 
|  | t.Errorf("Do = %v; want %v", got, want) | 
|  | } | 
|  | if err != nil { | 
|  | t.Errorf("Do error = %v", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestDoErr(t *testing.T) { | 
|  | var g Group | 
|  | someErr := errors.New("some error") | 
|  | v, err, _ := g.Do("key", func() (any, error) { | 
|  | return nil, someErr | 
|  | }) | 
|  | if err != someErr { | 
|  | t.Errorf("Do error = %v; want someErr %v", err, someErr) | 
|  | } | 
|  | if v != nil { | 
|  | t.Errorf("unexpected non-nil value %#v", v) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestDoDupSuppress(t *testing.T) { | 
|  | var g Group | 
|  | var wg1, wg2 sync.WaitGroup | 
|  | c := make(chan string, 1) | 
|  | var calls int32 | 
|  | fn := func() (any, error) { | 
|  | if atomic.AddInt32(&calls, 1) == 1 { | 
|  | // First invocation. | 
|  | wg1.Done() | 
|  | } | 
|  | v := <-c | 
|  | c <- v // pump; make available for any future calls | 
|  |  | 
|  | time.Sleep(10 * time.Millisecond) // let more goroutines enter Do | 
|  |  | 
|  | return v, nil | 
|  | } | 
|  |  | 
|  | const n = 10 | 
|  | wg1.Add(1) | 
|  | for i := 0; i < n; i++ { | 
|  | wg1.Add(1) | 
|  | wg2.Add(1) | 
|  | go func() { | 
|  | defer wg2.Done() | 
|  | wg1.Done() | 
|  | v, err, _ := g.Do("key", fn) | 
|  | if err != nil { | 
|  | t.Errorf("Do error: %v", err) | 
|  | return | 
|  | } | 
|  | if s, _ := v.(string); s != "bar" { | 
|  | t.Errorf("Do = %T %v; want %q", v, v, "bar") | 
|  | } | 
|  | }() | 
|  | } | 
|  | wg1.Wait() | 
|  | // At least one goroutine is in fn now and all of them have at | 
|  | // least reached the line before the Do. | 
|  | c <- "bar" | 
|  | wg2.Wait() | 
|  | if got := atomic.LoadInt32(&calls); got <= 0 || got >= n { | 
|  | t.Errorf("number of calls = %d; want over 0 and less than %d", got, n) | 
|  | } | 
|  | } |