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 5package singleflight 6 7import ( 8 "errors" 9 "fmt" 10 "sync" 11 "sync/atomic" 12 "testing" 13 "time" 14) 15 16func TestDo(t *testing.T) { 17 var g Group 18 v, err, _ := g.Do("key", func() (interface{}, error) { 19 return "bar", nil 20 }) 21 if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want { 22 t.Errorf("Do = %v; want %v", got, want) 23 } 24 if err != nil { 25 t.Errorf("Do error = %v", err) 26 } 27} 28 29func TestDoErr(t *testing.T) { 30 var g Group 31 someErr := errors.New("Some error") 32 v, err, _ := g.Do("key", func() (interface{}, error) { 33 return nil, someErr 34 }) 35 if err != someErr { 36 t.Errorf("Do error = %v; want someErr %v", err, someErr) 37 } 38 if v != nil { 39 t.Errorf("unexpected non-nil value %#v", v) 40 } 41} 42 43func TestDoDupSuppress(t *testing.T) { 44 var g Group 45 var wg1, wg2 sync.WaitGroup 46 c := make(chan string, 1) 47 var calls int32 48 fn := func() (interface{}, error) { 49 if atomic.AddInt32(&calls, 1) == 1 { 50 // First invocation. 51 wg1.Done() 52 } 53 v := <-c 54 c <- v // pump; make available for any future calls 55 56 time.Sleep(10 * time.Millisecond) // let more goroutines enter Do 57 58 return v, nil 59 } 60 61 const n = 10 62 wg1.Add(1) 63 for i := 0; i < n; i++ { 64 wg1.Add(1) 65 wg2.Add(1) 66 go func() { 67 defer wg2.Done() 68 wg1.Done() 69 v, err, _ := g.Do("key", fn) 70 if err != nil { 71 t.Errorf("Do error: %v", err) 72 return 73 } 74 if s, _ := v.(string); s != "bar" { 75 t.Errorf("Do = %T %v; want %q", v, v, "bar") 76 } 77 }() 78 } 79 wg1.Wait() 80 // At least one goroutine is in fn now and all of them have at 81 // least reached the line before the Do. 82 c <- "bar" 83 wg2.Wait() 84 if got := atomic.LoadInt32(&calls); got <= 0 || got >= n { 85 t.Errorf("number of calls = %d; want over 0 and less than %d", got, n) 86 } 87} 88