maps_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. package csync
  2. import (
  3. "encoding/json"
  4. "maps"
  5. "sync"
  6. "sync/atomic"
  7. "testing"
  8. "testing/synctest"
  9. "time"
  10. "github.com/stretchr/testify/require"
  11. )
  12. func TestNewMap(t *testing.T) {
  13. t.Parallel()
  14. m := NewMap[string, int]()
  15. require.NotNil(t, m)
  16. require.NotNil(t, m.inner)
  17. require.Equal(t, 0, m.Len())
  18. }
  19. func TestNewMapFrom(t *testing.T) {
  20. t.Parallel()
  21. original := map[string]int{
  22. "key1": 1,
  23. "key2": 2,
  24. }
  25. m := NewMapFrom(original)
  26. require.NotNil(t, m)
  27. require.Equal(t, original, m.inner)
  28. require.Equal(t, 2, m.Len())
  29. value, ok := m.Get("key1")
  30. require.True(t, ok)
  31. require.Equal(t, 1, value)
  32. }
  33. func TestNewLazyMap(t *testing.T) {
  34. t.Parallel()
  35. synctest.Test(t, func(t *testing.T) {
  36. t.Helper()
  37. waiter := sync.Mutex{}
  38. waiter.Lock()
  39. var loadCalled atomic.Bool
  40. loadFunc := func() map[string]int {
  41. waiter.Lock()
  42. defer waiter.Unlock()
  43. loadCalled.Store(true)
  44. return map[string]int{
  45. "key1": 1,
  46. "key2": 2,
  47. }
  48. }
  49. m := NewLazyMap(loadFunc)
  50. require.NotNil(t, m)
  51. waiter.Unlock() // Allow the load function to proceed
  52. time.Sleep(100 * time.Millisecond)
  53. require.True(t, loadCalled.Load())
  54. require.Equal(t, 2, m.Len())
  55. value, ok := m.Get("key1")
  56. require.True(t, ok)
  57. require.Equal(t, 1, value)
  58. })
  59. }
  60. func TestMap_Reset(t *testing.T) {
  61. t.Parallel()
  62. m := NewMapFrom(map[string]int{
  63. "a": 10,
  64. })
  65. m.Reset(map[string]int{
  66. "b": 20,
  67. })
  68. value, ok := m.Get("b")
  69. require.True(t, ok)
  70. require.Equal(t, 20, value)
  71. require.Equal(t, 1, m.Len())
  72. }
  73. func TestMap_Set(t *testing.T) {
  74. t.Parallel()
  75. m := NewMap[string, int]()
  76. m.Set("key1", 42)
  77. value, ok := m.Get("key1")
  78. require.True(t, ok)
  79. require.Equal(t, 42, value)
  80. require.Equal(t, 1, m.Len())
  81. m.Set("key1", 100)
  82. value, ok = m.Get("key1")
  83. require.True(t, ok)
  84. require.Equal(t, 100, value)
  85. require.Equal(t, 1, m.Len())
  86. }
  87. func TestMap_GetOrSet(t *testing.T) {
  88. t.Parallel()
  89. m := NewMap[string, int]()
  90. require.Equal(t, 42, m.GetOrSet("key1", func() int { return 42 }))
  91. require.Equal(t, 42, m.GetOrSet("key1", func() int { return 99999 }))
  92. require.Equal(t, 1, m.Len())
  93. }
  94. func TestMap_Get(t *testing.T) {
  95. t.Parallel()
  96. m := NewMap[string, int]()
  97. value, ok := m.Get("nonexistent")
  98. require.False(t, ok)
  99. require.Equal(t, 0, value)
  100. m.Set("key1", 42)
  101. value, ok = m.Get("key1")
  102. require.True(t, ok)
  103. require.Equal(t, 42, value)
  104. }
  105. func TestMap_Del(t *testing.T) {
  106. t.Parallel()
  107. m := NewMap[string, int]()
  108. m.Set("key1", 42)
  109. m.Set("key2", 100)
  110. require.Equal(t, 2, m.Len())
  111. m.Del("key1")
  112. _, ok := m.Get("key1")
  113. require.False(t, ok)
  114. require.Equal(t, 1, m.Len())
  115. value, ok := m.Get("key2")
  116. require.True(t, ok)
  117. require.Equal(t, 100, value)
  118. m.Del("nonexistent")
  119. require.Equal(t, 1, m.Len())
  120. }
  121. func TestMap_Len(t *testing.T) {
  122. t.Parallel()
  123. m := NewMap[string, int]()
  124. require.Equal(t, 0, m.Len())
  125. m.Set("key1", 1)
  126. require.Equal(t, 1, m.Len())
  127. m.Set("key2", 2)
  128. require.Equal(t, 2, m.Len())
  129. m.Del("key1")
  130. require.Equal(t, 1, m.Len())
  131. m.Del("key2")
  132. require.Equal(t, 0, m.Len())
  133. }
  134. func TestMap_Take(t *testing.T) {
  135. t.Parallel()
  136. m := NewMap[string, int]()
  137. m.Set("key1", 42)
  138. m.Set("key2", 100)
  139. require.Equal(t, 2, m.Len())
  140. value, ok := m.Take("key1")
  141. require.True(t, ok)
  142. require.Equal(t, 42, value)
  143. require.Equal(t, 1, m.Len())
  144. _, exists := m.Get("key1")
  145. require.False(t, exists)
  146. value, ok = m.Get("key2")
  147. require.True(t, ok)
  148. require.Equal(t, 100, value)
  149. }
  150. func TestMap_Take_NonexistentKey(t *testing.T) {
  151. t.Parallel()
  152. m := NewMap[string, int]()
  153. m.Set("key1", 42)
  154. value, ok := m.Take("nonexistent")
  155. require.False(t, ok)
  156. require.Equal(t, 0, value)
  157. require.Equal(t, 1, m.Len())
  158. value, ok = m.Get("key1")
  159. require.True(t, ok)
  160. require.Equal(t, 42, value)
  161. }
  162. func TestMap_Take_EmptyMap(t *testing.T) {
  163. t.Parallel()
  164. m := NewMap[string, int]()
  165. value, ok := m.Take("key1")
  166. require.False(t, ok)
  167. require.Equal(t, 0, value)
  168. require.Equal(t, 0, m.Len())
  169. }
  170. func TestMap_Take_SameKeyTwice(t *testing.T) {
  171. t.Parallel()
  172. m := NewMap[string, int]()
  173. m.Set("key1", 42)
  174. value, ok := m.Take("key1")
  175. require.True(t, ok)
  176. require.Equal(t, 42, value)
  177. require.Equal(t, 0, m.Len())
  178. value, ok = m.Take("key1")
  179. require.False(t, ok)
  180. require.Equal(t, 0, value)
  181. require.Equal(t, 0, m.Len())
  182. }
  183. func TestMap_Seq2(t *testing.T) {
  184. t.Parallel()
  185. m := NewMap[string, int]()
  186. m.Set("key1", 1)
  187. m.Set("key2", 2)
  188. m.Set("key3", 3)
  189. collected := maps.Collect(m.Seq2())
  190. require.Equal(t, 3, len(collected))
  191. require.Equal(t, 1, collected["key1"])
  192. require.Equal(t, 2, collected["key2"])
  193. require.Equal(t, 3, collected["key3"])
  194. }
  195. func TestMap_Seq2_EarlyReturn(t *testing.T) {
  196. t.Parallel()
  197. m := NewMap[string, int]()
  198. m.Set("key1", 1)
  199. m.Set("key2", 2)
  200. m.Set("key3", 3)
  201. count := 0
  202. for range m.Seq2() {
  203. count++
  204. if count == 2 {
  205. break
  206. }
  207. }
  208. require.Equal(t, 2, count)
  209. }
  210. func TestMap_Seq2_EmptyMap(t *testing.T) {
  211. t.Parallel()
  212. m := NewMap[string, int]()
  213. count := 0
  214. for range m.Seq2() {
  215. count++
  216. }
  217. require.Equal(t, 0, count)
  218. }
  219. func TestMap_Seq(t *testing.T) {
  220. t.Parallel()
  221. m := NewMap[string, int]()
  222. m.Set("key1", 1)
  223. m.Set("key2", 2)
  224. m.Set("key3", 3)
  225. collected := make([]int, 0)
  226. for v := range m.Seq() {
  227. collected = append(collected, v)
  228. }
  229. require.Equal(t, 3, len(collected))
  230. require.Contains(t, collected, 1)
  231. require.Contains(t, collected, 2)
  232. require.Contains(t, collected, 3)
  233. }
  234. func TestMap_Seq_EarlyReturn(t *testing.T) {
  235. t.Parallel()
  236. m := NewMap[string, int]()
  237. m.Set("key1", 1)
  238. m.Set("key2", 2)
  239. m.Set("key3", 3)
  240. count := 0
  241. for range m.Seq() {
  242. count++
  243. if count == 2 {
  244. break
  245. }
  246. }
  247. require.Equal(t, 2, count)
  248. }
  249. func TestMap_Seq_EmptyMap(t *testing.T) {
  250. t.Parallel()
  251. m := NewMap[string, int]()
  252. count := 0
  253. for range m.Seq() {
  254. count++
  255. }
  256. require.Equal(t, 0, count)
  257. }
  258. func TestMap_MarshalJSON(t *testing.T) {
  259. t.Parallel()
  260. m := NewMap[string, int]()
  261. m.Set("key1", 1)
  262. m.Set("key2", 2)
  263. data, err := json.Marshal(m)
  264. require.NoError(t, err)
  265. result := &Map[string, int]{}
  266. err = json.Unmarshal(data, result)
  267. require.NoError(t, err)
  268. require.Equal(t, 2, result.Len())
  269. v1, _ := result.Get("key1")
  270. v2, _ := result.Get("key2")
  271. require.Equal(t, 1, v1)
  272. require.Equal(t, 2, v2)
  273. }
  274. func TestMap_MarshalJSON_EmptyMap(t *testing.T) {
  275. t.Parallel()
  276. m := NewMap[string, int]()
  277. data, err := json.Marshal(m)
  278. require.NoError(t, err)
  279. require.Equal(t, "{}", string(data))
  280. }
  281. func TestMap_UnmarshalJSON(t *testing.T) {
  282. t.Parallel()
  283. jsonData := `{"key1": 1, "key2": 2}`
  284. m := NewMap[string, int]()
  285. err := json.Unmarshal([]byte(jsonData), m)
  286. require.NoError(t, err)
  287. require.Equal(t, 2, m.Len())
  288. value, ok := m.Get("key1")
  289. require.True(t, ok)
  290. require.Equal(t, 1, value)
  291. value, ok = m.Get("key2")
  292. require.True(t, ok)
  293. require.Equal(t, 2, value)
  294. }
  295. func TestMap_UnmarshalJSON_EmptyJSON(t *testing.T) {
  296. t.Parallel()
  297. jsonData := `{}`
  298. m := NewMap[string, int]()
  299. err := json.Unmarshal([]byte(jsonData), m)
  300. require.NoError(t, err)
  301. require.Equal(t, 0, m.Len())
  302. }
  303. func TestMap_UnmarshalJSON_InvalidJSON(t *testing.T) {
  304. t.Parallel()
  305. jsonData := `{"key1": 1, "key2":}`
  306. m := NewMap[string, int]()
  307. err := json.Unmarshal([]byte(jsonData), m)
  308. require.Error(t, err)
  309. }
  310. func TestMap_UnmarshalJSON_OverwritesExistingData(t *testing.T) {
  311. t.Parallel()
  312. m := NewMap[string, int]()
  313. m.Set("existing", 999)
  314. jsonData := `{"key1": 1, "key2": 2}`
  315. err := json.Unmarshal([]byte(jsonData), m)
  316. require.NoError(t, err)
  317. require.Equal(t, 2, m.Len())
  318. _, ok := m.Get("existing")
  319. require.False(t, ok)
  320. value, ok := m.Get("key1")
  321. require.True(t, ok)
  322. require.Equal(t, 1, value)
  323. }
  324. func TestMap_JSONRoundTrip(t *testing.T) {
  325. t.Parallel()
  326. original := NewMap[string, int]()
  327. original.Set("key1", 1)
  328. original.Set("key2", 2)
  329. original.Set("key3", 3)
  330. data, err := json.Marshal(original)
  331. require.NoError(t, err)
  332. restored := NewMap[string, int]()
  333. err = json.Unmarshal(data, restored)
  334. require.NoError(t, err)
  335. require.Equal(t, original.Len(), restored.Len())
  336. for k, v := range original.Seq2() {
  337. restoredValue, ok := restored.Get(k)
  338. require.True(t, ok)
  339. require.Equal(t, v, restoredValue)
  340. }
  341. }
  342. func TestMap_ConcurrentAccess(t *testing.T) {
  343. t.Parallel()
  344. m := NewMap[int, int]()
  345. const numGoroutines = 100
  346. const numOperations = 100
  347. var wg sync.WaitGroup
  348. wg.Add(numGoroutines)
  349. for i := range numGoroutines {
  350. go func(id int) {
  351. defer wg.Done()
  352. for j := range numOperations {
  353. key := id*numOperations + j
  354. m.Set(key, key*2)
  355. value, ok := m.Get(key)
  356. require.True(t, ok)
  357. require.Equal(t, key*2, value)
  358. }
  359. }(i)
  360. }
  361. wg.Wait()
  362. require.Equal(t, numGoroutines*numOperations, m.Len())
  363. }
  364. func TestMap_ConcurrentReadWrite(t *testing.T) {
  365. t.Parallel()
  366. m := NewMap[int, int]()
  367. const numReaders = 50
  368. const numWriters = 50
  369. const numOperations = 100
  370. for i := range 1000 {
  371. m.Set(i, i)
  372. }
  373. var wg sync.WaitGroup
  374. wg.Add(numReaders + numWriters)
  375. for range numReaders {
  376. go func() {
  377. defer wg.Done()
  378. for j := range numOperations {
  379. key := j % 1000
  380. value, ok := m.Get(key)
  381. if ok {
  382. require.Equal(t, key, value)
  383. }
  384. _ = m.Len()
  385. }
  386. }()
  387. }
  388. for i := range numWriters {
  389. go func(id int) {
  390. defer wg.Done()
  391. for j := range numOperations {
  392. key := 1000 + id*numOperations + j
  393. m.Set(key, key)
  394. if j%10 == 0 {
  395. m.Del(key)
  396. }
  397. }
  398. }(i)
  399. }
  400. wg.Wait()
  401. }
  402. func TestMap_ConcurrentSeq2(t *testing.T) {
  403. t.Parallel()
  404. m := NewMap[int, int]()
  405. for i := range 100 {
  406. m.Set(i, i*2)
  407. }
  408. var wg sync.WaitGroup
  409. const numIterators = 10
  410. wg.Add(numIterators)
  411. for range numIterators {
  412. go func() {
  413. defer wg.Done()
  414. count := 0
  415. for k, v := range m.Seq2() {
  416. require.Equal(t, k*2, v)
  417. count++
  418. }
  419. require.Equal(t, 100, count)
  420. }()
  421. }
  422. wg.Wait()
  423. }
  424. func TestMap_ConcurrentSeq(t *testing.T) {
  425. t.Parallel()
  426. m := NewMap[int, int]()
  427. for i := range 100 {
  428. m.Set(i, i*2)
  429. }
  430. var wg sync.WaitGroup
  431. const numIterators = 10
  432. wg.Add(numIterators)
  433. for range numIterators {
  434. go func() {
  435. defer wg.Done()
  436. count := 0
  437. values := make(map[int]bool)
  438. for v := range m.Seq() {
  439. values[v] = true
  440. count++
  441. }
  442. require.Equal(t, 100, count)
  443. for i := range 100 {
  444. require.True(t, values[i*2])
  445. }
  446. }()
  447. }
  448. wg.Wait()
  449. }
  450. func TestMap_ConcurrentTake(t *testing.T) {
  451. t.Parallel()
  452. m := NewMap[int, int]()
  453. const numItems = 1000
  454. for i := range numItems {
  455. m.Set(i, i*2)
  456. }
  457. var wg sync.WaitGroup
  458. const numWorkers = 10
  459. taken := make([][]int, numWorkers)
  460. wg.Add(numWorkers)
  461. for i := range numWorkers {
  462. go func(workerID int) {
  463. defer wg.Done()
  464. taken[workerID] = make([]int, 0)
  465. for j := workerID; j < numItems; j += numWorkers {
  466. if value, ok := m.Take(j); ok {
  467. taken[workerID] = append(taken[workerID], value)
  468. }
  469. }
  470. }(i)
  471. }
  472. wg.Wait()
  473. require.Equal(t, 0, m.Len())
  474. allTaken := make(map[int]bool)
  475. for _, workerTaken := range taken {
  476. for _, value := range workerTaken {
  477. require.False(t, allTaken[value], "Value %d was taken multiple times", value)
  478. allTaken[value] = true
  479. }
  480. }
  481. require.Equal(t, numItems, len(allTaken))
  482. for i := range numItems {
  483. require.True(t, allTaken[i*2], "Expected value %d to be taken", i*2)
  484. }
  485. }
  486. func TestMap_TypeSafety(t *testing.T) {
  487. t.Parallel()
  488. stringIntMap := NewMap[string, int]()
  489. stringIntMap.Set("key", 42)
  490. value, ok := stringIntMap.Get("key")
  491. require.True(t, ok)
  492. require.Equal(t, 42, value)
  493. intStringMap := NewMap[int, string]()
  494. intStringMap.Set(42, "value")
  495. strValue, ok := intStringMap.Get(42)
  496. require.True(t, ok)
  497. require.Equal(t, "value", strValue)
  498. structMap := NewMap[string, struct{ Name string }]()
  499. structMap.Set("key", struct{ Name string }{Name: "test"})
  500. structValue, ok := structMap.Get("key")
  501. require.True(t, ok)
  502. require.Equal(t, "test", structValue.Name)
  503. }
  504. func TestMap_InterfaceCompliance(t *testing.T) {
  505. t.Parallel()
  506. var _ json.Marshaler = &Map[string, any]{}
  507. var _ json.Unmarshaler = &Map[string, any]{}
  508. }
  509. func BenchmarkMap_Set(b *testing.B) {
  510. m := NewMap[int, int]()
  511. for i := 0; b.Loop(); i++ {
  512. m.Set(i, i*2)
  513. }
  514. }
  515. func BenchmarkMap_Get(b *testing.B) {
  516. m := NewMap[int, int]()
  517. for i := range 1000 {
  518. m.Set(i, i*2)
  519. }
  520. for i := 0; b.Loop(); i++ {
  521. m.Get(i % 1000)
  522. }
  523. }
  524. func BenchmarkMap_Seq2(b *testing.B) {
  525. m := NewMap[int, int]()
  526. for i := range 1000 {
  527. m.Set(i, i*2)
  528. }
  529. for b.Loop() {
  530. for range m.Seq2() {
  531. }
  532. }
  533. }
  534. func BenchmarkMap_Seq(b *testing.B) {
  535. m := NewMap[int, int]()
  536. for i := range 1000 {
  537. m.Set(i, i*2)
  538. }
  539. for b.Loop() {
  540. for range m.Seq() {
  541. }
  542. }
  543. }
  544. func BenchmarkMap_Take(b *testing.B) {
  545. m := NewMap[int, int]()
  546. for i := range 1000 {
  547. m.Set(i, i*2)
  548. }
  549. b.ResetTimer()
  550. for i := 0; b.Loop(); i++ {
  551. key := i % 1000
  552. m.Take(key)
  553. if i%1000 == 999 {
  554. b.StopTimer()
  555. for j := range 1000 {
  556. m.Set(j, j*2)
  557. }
  558. b.StartTimer()
  559. }
  560. }
  561. }
  562. func BenchmarkMap_ConcurrentReadWrite(b *testing.B) {
  563. m := NewMap[int, int]()
  564. for i := range 1000 {
  565. m.Set(i, i*2)
  566. }
  567. b.ResetTimer()
  568. b.RunParallel(func(pb *testing.PB) {
  569. i := 0
  570. for pb.Next() {
  571. if i%2 == 0 {
  572. m.Get(i % 1000)
  573. } else {
  574. m.Set(i+1000, i*2)
  575. }
  576. i++
  577. }
  578. })
  579. }