bufferpool_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright (C) 2019 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package protocol
  7. import (
  8. "sync"
  9. "testing"
  10. "time"
  11. "github.com/syncthing/syncthing/lib/rand"
  12. )
  13. func TestGetBucketNumbers(t *testing.T) {
  14. cases := []struct {
  15. size int
  16. bkt int
  17. panics bool
  18. }{
  19. {size: 1024, bkt: 0},
  20. {size: MinBlockSize, bkt: 0},
  21. {size: MinBlockSize + 1, bkt: 1},
  22. {size: 2*MinBlockSize - 1, bkt: 1},
  23. {size: 2 * MinBlockSize, bkt: 1},
  24. {size: 2*MinBlockSize + 1, bkt: 2},
  25. {size: MaxBlockSize, bkt: len(BlockSizes) - 1},
  26. {size: MaxBlockSize + 1, panics: true},
  27. }
  28. for _, tc := range cases {
  29. if tc.panics {
  30. shouldPanic(t, func() { getBucketForLen(tc.size) })
  31. } else {
  32. res := getBucketForLen(tc.size)
  33. if res != tc.bkt {
  34. t.Errorf("block of size %d should get from bucket %d, not %d", tc.size, tc.bkt, res)
  35. }
  36. }
  37. }
  38. }
  39. func TestPutBucketNumbers(t *testing.T) {
  40. cases := []struct {
  41. size int
  42. bkt int
  43. panics bool
  44. }{
  45. {size: 1024, panics: true},
  46. {size: MinBlockSize, bkt: 0},
  47. {size: MinBlockSize + 1, panics: true},
  48. {size: 2 * MinBlockSize, bkt: 1},
  49. {size: MaxBlockSize, bkt: len(BlockSizes) - 1},
  50. {size: MaxBlockSize + 1, panics: true},
  51. }
  52. for _, tc := range cases {
  53. if tc.panics {
  54. shouldPanic(t, func() { putBucketForCap(tc.size) })
  55. } else {
  56. res := putBucketForCap(tc.size)
  57. if res != tc.bkt {
  58. t.Errorf("block of size %d should put into bucket %d, not %d", tc.size, tc.bkt, res)
  59. }
  60. }
  61. }
  62. }
  63. func TestStressBufferPool(t *testing.T) {
  64. if testing.Short() {
  65. t.Skip()
  66. }
  67. const routines = 10
  68. const runtime = 2 * time.Second
  69. bp := newBufferPool()
  70. t0 := time.Now()
  71. var wg sync.WaitGroup
  72. fail := make(chan struct{}, routines)
  73. for i := 0; i < routines; i++ {
  74. wg.Add(1)
  75. go func() {
  76. defer wg.Done()
  77. for time.Since(t0) < runtime {
  78. blocks := make([][]byte, 10)
  79. for i := range blocks {
  80. // Request a block of random size with the range
  81. // covering smaller-than-min to larger-than-max and
  82. // everything in between.
  83. want := rand.Intn(1.5 * MaxBlockSize)
  84. blocks[i] = bp.Get(want)
  85. if len(blocks[i]) != want {
  86. fail <- struct{}{}
  87. return
  88. }
  89. }
  90. for i := range blocks {
  91. bp.Put(blocks[i])
  92. }
  93. }
  94. }()
  95. }
  96. wg.Wait()
  97. select {
  98. case <-fail:
  99. t.Fatal("a block was bad size")
  100. default:
  101. }
  102. t.Log(bp.puts.Load(), bp.skips.Load(), bp.misses.Load(), bp.hits)
  103. if bp.puts.Load() == 0 || bp.skips.Load() == 0 || bp.misses.Load() == 0 {
  104. t.Error("didn't exercise some paths")
  105. }
  106. var hits int64
  107. for i := range bp.hits {
  108. hits += bp.hits[i].Load()
  109. }
  110. if hits == 0 {
  111. t.Error("didn't exercise some paths")
  112. }
  113. }
  114. func shouldPanic(t *testing.T, fn func()) {
  115. defer func() {
  116. if r := recover(); r == nil {
  117. t.Errorf("did not panic")
  118. }
  119. }()
  120. fn()
  121. }