syncs_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package syncs
  4. import (
  5. "context"
  6. "io"
  7. "os"
  8. "sync"
  9. "testing"
  10. "github.com/google/go-cmp/cmp"
  11. )
  12. func TestAtomicValue(t *testing.T) {
  13. {
  14. // Always wrapping should not allocate for simple values
  15. // because wrappedValue[T] has the same memory layout as T.
  16. var v AtomicValue[bool]
  17. bools := []bool{true, false}
  18. if n := int(testing.AllocsPerRun(1000, func() {
  19. for _, b := range bools {
  20. v.Store(b)
  21. }
  22. })); n != 0 {
  23. t.Errorf("AllocsPerRun = %d, want 0", n)
  24. }
  25. }
  26. {
  27. var v AtomicValue[int]
  28. got, gotOk := v.LoadOk()
  29. if got != 0 || gotOk {
  30. t.Fatalf("LoadOk = (%v, %v), want (0, false)", got, gotOk)
  31. }
  32. v.Store(1)
  33. got, gotOk = v.LoadOk()
  34. if got != 1 || !gotOk {
  35. t.Fatalf("LoadOk = (%v, %v), want (1, true)", got, gotOk)
  36. }
  37. }
  38. {
  39. var v AtomicValue[error]
  40. got, gotOk := v.LoadOk()
  41. if got != nil || gotOk {
  42. t.Fatalf("LoadOk = (%v, %v), want (nil, false)", got, gotOk)
  43. }
  44. v.Store(io.EOF)
  45. got, gotOk = v.LoadOk()
  46. if got != io.EOF || !gotOk {
  47. t.Fatalf("LoadOk = (%v, %v), want (EOF, true)", got, gotOk)
  48. }
  49. err := &os.PathError{}
  50. v.Store(err)
  51. got, gotOk = v.LoadOk()
  52. if got != err || !gotOk {
  53. t.Fatalf("LoadOk = (%v, %v), want (%v, true)", got, gotOk, err)
  54. }
  55. v.Store(nil)
  56. got, gotOk = v.LoadOk()
  57. if got != nil || !gotOk {
  58. t.Fatalf("LoadOk = (%v, %v), want (nil, true)", got, gotOk)
  59. }
  60. }
  61. }
  62. func TestWaitGroupChan(t *testing.T) {
  63. wg := NewWaitGroupChan()
  64. wantNotDone := func() {
  65. t.Helper()
  66. select {
  67. case <-wg.DoneChan():
  68. t.Fatal("done too early")
  69. default:
  70. }
  71. }
  72. wantDone := func() {
  73. t.Helper()
  74. select {
  75. case <-wg.DoneChan():
  76. default:
  77. t.Fatal("expected to be done")
  78. }
  79. }
  80. wg.Add(2)
  81. wantNotDone()
  82. wg.Decr()
  83. wantNotDone()
  84. wg.Decr()
  85. wantDone()
  86. wantDone()
  87. }
  88. func TestClosedChan(t *testing.T) {
  89. ch := ClosedChan()
  90. for range 2 {
  91. select {
  92. case <-ch:
  93. default:
  94. t.Fatal("not closed")
  95. }
  96. }
  97. }
  98. func TestSemaphore(t *testing.T) {
  99. s := NewSemaphore(2)
  100. s.Acquire()
  101. if !s.TryAcquire() {
  102. t.Fatal("want true")
  103. }
  104. if s.TryAcquire() {
  105. t.Fatal("want false")
  106. }
  107. ctx, cancel := context.WithCancel(context.Background())
  108. cancel()
  109. if s.AcquireContext(ctx) {
  110. t.Fatal("want false")
  111. }
  112. s.Release()
  113. if !s.AcquireContext(context.Background()) {
  114. t.Fatal("want true")
  115. }
  116. s.Release()
  117. s.Release()
  118. }
  119. func TestMap(t *testing.T) {
  120. var m Map[string, int]
  121. if v, ok := m.Load("noexist"); v != 0 || ok {
  122. t.Errorf(`Load("noexist") = (%v, %v), want (0, false)`, v, ok)
  123. }
  124. m.LoadFunc("noexist", func(v int, ok bool) {
  125. if v != 0 || ok {
  126. t.Errorf(`LoadFunc("noexist") = (%v, %v), want (0, false)`, v, ok)
  127. }
  128. })
  129. m.Store("one", 1)
  130. if v, ok := m.LoadOrStore("one", -1); v != 1 || !ok {
  131. t.Errorf(`LoadOrStore("one", 1) = (%v, %v), want (1, true)`, v, ok)
  132. }
  133. if v, ok := m.Load("one"); v != 1 || !ok {
  134. t.Errorf(`Load("one") = (%v, %v), want (1, true)`, v, ok)
  135. }
  136. m.LoadFunc("one", func(v int, ok bool) {
  137. if v != 1 || !ok {
  138. t.Errorf(`LoadFunc("one") = (%v, %v), want (1, true)`, v, ok)
  139. }
  140. })
  141. if v, ok := m.LoadOrStore("two", 2); v != 2 || ok {
  142. t.Errorf(`LoadOrStore("two", 2) = (%v, %v), want (2, false)`, v, ok)
  143. }
  144. if v, ok := m.LoadOrInit("three", func() int { return 3 }); v != 3 || ok {
  145. t.Errorf(`LoadOrInit("three", 3) = (%v, %v), want (3, true)`, v, ok)
  146. }
  147. got := map[string]int{}
  148. want := map[string]int{"one": 1, "two": 2, "three": 3}
  149. m.Range(func(k string, v int) bool {
  150. got[k] = v
  151. return true
  152. })
  153. if d := cmp.Diff(got, want); d != "" {
  154. t.Errorf("Range mismatch (-got +want):\n%s", d)
  155. }
  156. if v, ok := m.LoadAndDelete("two"); v != 2 || !ok {
  157. t.Errorf(`LoadAndDelete("two) = (%v, %v), want (2, true)`, v, ok)
  158. }
  159. if v, ok := m.LoadAndDelete("two"); v != 0 || ok {
  160. t.Errorf(`LoadAndDelete("two) = (%v, %v), want (0, false)`, v, ok)
  161. }
  162. m.Delete("three")
  163. m.Delete("one")
  164. m.Delete("noexist")
  165. got = map[string]int{}
  166. want = map[string]int{}
  167. m.Range(func(k string, v int) bool {
  168. got[k] = v
  169. return true
  170. })
  171. if d := cmp.Diff(got, want); d != "" {
  172. t.Errorf("Range mismatch (-got +want):\n%s", d)
  173. }
  174. t.Run("LoadOrStore", func(t *testing.T) {
  175. var m Map[string, string]
  176. var wg sync.WaitGroup
  177. wg.Add(2)
  178. var ok1, ok2 bool
  179. go func() {
  180. defer wg.Done()
  181. _, ok1 = m.LoadOrStore("", "")
  182. }()
  183. go func() {
  184. defer wg.Done()
  185. _, ok2 = m.LoadOrStore("", "")
  186. }()
  187. wg.Wait()
  188. if ok1 == ok2 {
  189. t.Errorf("exactly one LoadOrStore should load")
  190. }
  191. })
  192. t.Run("Clear", func(t *testing.T) {
  193. var m Map[string, string]
  194. _, _ = m.LoadOrStore("a", "1")
  195. _, _ = m.LoadOrStore("b", "2")
  196. _, _ = m.LoadOrStore("c", "3")
  197. _, _ = m.LoadOrStore("d", "4")
  198. _, _ = m.LoadOrStore("e", "5")
  199. if m.Len() != 5 {
  200. t.Errorf("Len after loading want=5 got=%d", m.Len())
  201. }
  202. m.Clear()
  203. if m.Len() != 0 {
  204. t.Errorf("Len after Clear want=0 got=%d", m.Len())
  205. }
  206. })
  207. t.Run("Swap", func(t *testing.T) {
  208. var m Map[string, string]
  209. m.Store("hello", "world")
  210. if got, want := m.Swap("hello", "world2"), "world"; got != want {
  211. t.Errorf("got old value %q, want %q", got, want)
  212. }
  213. if got := m.Swap("empty", "foo"); got != "" {
  214. t.Errorf("got old value %q, want empty string", got)
  215. }
  216. })
  217. }