sharedcalls_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package syncx
  2. import (
  3. "errors"
  4. "fmt"
  5. "sync"
  6. "sync/atomic"
  7. "testing"
  8. "time"
  9. )
  10. func TestExclusiveCallDo(t *testing.T) {
  11. g := NewSharedCalls()
  12. v, err := g.Do("key", func() (interface{}, error) {
  13. return "bar", nil
  14. })
  15. if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
  16. t.Errorf("Do = %v; want %v", got, want)
  17. }
  18. if err != nil {
  19. t.Errorf("Do error = %v", err)
  20. }
  21. }
  22. func TestExclusiveCallDoErr(t *testing.T) {
  23. g := NewSharedCalls()
  24. someErr := errors.New("some error")
  25. v, err := g.Do("key", func() (interface{}, error) {
  26. return nil, someErr
  27. })
  28. if err != someErr {
  29. t.Errorf("Do error = %v; want someErr", err)
  30. }
  31. if v != nil {
  32. t.Errorf("unexpected non-nil value %#v", v)
  33. }
  34. }
  35. func TestExclusiveCallDoDupSuppress(t *testing.T) {
  36. g := NewSharedCalls()
  37. c := make(chan string)
  38. var calls int32
  39. fn := func() (interface{}, error) {
  40. atomic.AddInt32(&calls, 1)
  41. return <-c, nil
  42. }
  43. const n = 10
  44. var wg sync.WaitGroup
  45. for i := 0; i < n; i++ {
  46. wg.Add(1)
  47. go func() {
  48. v, err := g.Do("key", fn)
  49. if err != nil {
  50. t.Errorf("Do error: %v", err)
  51. }
  52. if v.(string) != "bar" {
  53. t.Errorf("got %q; want %q", v, "bar")
  54. }
  55. wg.Done()
  56. }()
  57. }
  58. time.Sleep(100 * time.Millisecond) // let goroutines above block
  59. c <- "bar"
  60. wg.Wait()
  61. if got := atomic.LoadInt32(&calls); got != 1 {
  62. t.Errorf("number of calls = %d; want 1", got)
  63. }
  64. }
  65. func TestExclusiveCallDoDiffDupSuppress(t *testing.T) {
  66. g := NewSharedCalls()
  67. broadcast := make(chan struct{})
  68. var calls int32
  69. tests := []string{"e", "a", "e", "a", "b", "c", "b", "a", "c", "d", "b", "c", "d"}
  70. var wg sync.WaitGroup
  71. for _, key := range tests {
  72. wg.Add(1)
  73. go func(k string) {
  74. <-broadcast // get all goroutines ready
  75. _, err := g.Do(k, func() (interface{}, error) {
  76. atomic.AddInt32(&calls, 1)
  77. time.Sleep(10 * time.Millisecond)
  78. return nil, nil
  79. })
  80. if err != nil {
  81. t.Errorf("Do error: %v", err)
  82. }
  83. wg.Done()
  84. }(key)
  85. }
  86. time.Sleep(100 * time.Millisecond) // let goroutines above block
  87. close(broadcast)
  88. wg.Wait()
  89. if got := atomic.LoadInt32(&calls); got != 5 { // five letters
  90. t.Errorf("number of calls = %d; want 5", got)
  91. }
  92. }
  93. func TestExclusiveCallDoExDupSuppress(t *testing.T) {
  94. g := NewSharedCalls()
  95. c := make(chan string)
  96. var calls int32
  97. fn := func() (interface{}, error) {
  98. atomic.AddInt32(&calls, 1)
  99. return <-c, nil
  100. }
  101. const n = 10
  102. var wg sync.WaitGroup
  103. var freshes int32
  104. for i := 0; i < n; i++ {
  105. wg.Add(1)
  106. go func() {
  107. v, fresh, err := g.DoEx("key", fn)
  108. if err != nil {
  109. t.Errorf("Do error: %v", err)
  110. }
  111. if fresh {
  112. atomic.AddInt32(&freshes, 1)
  113. }
  114. if v.(string) != "bar" {
  115. t.Errorf("got %q; want %q", v, "bar")
  116. }
  117. wg.Done()
  118. }()
  119. }
  120. time.Sleep(100 * time.Millisecond) // let goroutines above block
  121. c <- "bar"
  122. wg.Wait()
  123. if got := atomic.LoadInt32(&calls); got != 1 {
  124. t.Errorf("number of calls = %d; want 1", got)
  125. }
  126. if got := atomic.LoadInt32(&freshes); got != 1 {
  127. t.Errorf("freshes = %d; want 1", got)
  128. }
  129. }