bufferpool_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  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.Go(func() {
  75. for time.Since(t0) < runtime {
  76. blocks := make([][]byte, 10)
  77. for i := range blocks {
  78. // Request a block of random size with the range
  79. // covering smaller-than-min to larger-than-max and
  80. // everything in between.
  81. want := rand.Intn(1.5 * MaxBlockSize)
  82. blocks[i] = bp.Get(want)
  83. if len(blocks[i]) != want {
  84. fail <- struct{}{}
  85. return
  86. }
  87. }
  88. for i := range blocks {
  89. bp.Put(blocks[i])
  90. }
  91. }
  92. })
  93. }
  94. wg.Wait()
  95. select {
  96. case <-fail:
  97. t.Fatal("a block was bad size")
  98. default:
  99. }
  100. t.Log(bp.puts.Load(), bp.skips.Load(), bp.misses.Load(), bp.hits)
  101. if bp.puts.Load() == 0 || bp.skips.Load() == 0 || bp.misses.Load() == 0 {
  102. t.Error("didn't exercise some paths")
  103. }
  104. var hits int64
  105. for i := range bp.hits {
  106. hits += bp.hits[i].Load()
  107. }
  108. if hits == 0 {
  109. t.Error("didn't exercise some paths")
  110. }
  111. }
  112. func shouldPanic(t *testing.T, fn func()) {
  113. defer func() {
  114. if r := recover(); r == nil {
  115. t.Errorf("did not panic")
  116. }
  117. }()
  118. fn()
  119. }