maps_test.go 12 KB

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