github.com/decred/dcrlnd@v0.7.6/pool/recycle_test.go (about) 1 package pool_test 2 3 import ( 4 "bytes" 5 "testing" 6 "time" 7 8 "github.com/decred/dcrlnd/buffer" 9 "github.com/decred/dcrlnd/pool" 10 ) 11 12 type mockRecycler bool 13 14 func (m *mockRecycler) Recycle() { 15 *m = false 16 } 17 18 // TestRecyclers verifies that known recyclable types properly return to their 19 // zero-value after invoking Recycle. 20 func TestRecyclers(t *testing.T) { 21 tests := []struct { 22 name string 23 newItem func() interface{} 24 }{ 25 { 26 "mock recycler", 27 func() interface{} { return new(mockRecycler) }, 28 }, 29 { 30 "write_buffer", 31 func() interface{} { return new(buffer.Write) }, 32 }, 33 { 34 "read_buffer", 35 func() interface{} { return new(buffer.Read) }, 36 }, 37 } 38 39 for _, test := range tests { 40 t.Run(test.name, func(t *testing.T) { 41 // Initialize the Recycler to test. 42 r := test.newItem().(pool.Recycler) 43 44 // Dirty the item. 45 dirtyGeneric(t, r) 46 47 // Invoke Recycle to clear the item. 48 r.Recycle() 49 50 // Assert the item is now clean. 51 isCleanGeneric(t, r) 52 }) 53 } 54 } 55 56 type recyclePoolTest struct { 57 name string 58 newPool func() interface{} 59 } 60 61 // TestGenericRecyclePoolTests generically tests that pools derived from the 62 // base Recycle pool properly are properly configured. 63 func TestConcreteRecyclePoolTests(t *testing.T) { 64 const ( 65 gcInterval = time.Second 66 expiryInterval = 250 * time.Millisecond 67 ) 68 69 tests := []recyclePoolTest{ 70 { 71 name: "write buffer pool", 72 newPool: func() interface{} { 73 return pool.NewWriteBuffer( 74 gcInterval, expiryInterval, 75 ) 76 }, 77 }, 78 { 79 name: "read buffer pool", 80 newPool: func() interface{} { 81 return pool.NewReadBuffer( 82 gcInterval, expiryInterval, 83 ) 84 }, 85 }, 86 } 87 88 for _, test := range tests { 89 t.Run(test.name, func(t *testing.T) { 90 testRecyclePool(t, test) 91 }) 92 } 93 } 94 95 func testRecyclePool(t *testing.T, test recyclePoolTest) { 96 p := test.newPool() 97 98 // Take an item from the pool. 99 r1 := takeGeneric(t, p) 100 101 // Dirty the item. 102 dirtyGeneric(t, r1) 103 104 // Return the item to the pool. 105 returnGeneric(t, p, r1) 106 107 // Take items from the pool until we find the original. We expect at 108 // most two, in the event that a fresh item is populated after the 109 // first is taken. 110 for i := 0; i < 2; i++ { 111 // Wait a small duration to ensure the tests are reliable, and 112 // don't to active the non-blocking case unintentionally. 113 <-time.After(time.Millisecond) 114 115 r2 := takeGeneric(t, p) 116 117 // Take an item, skipping those whose pointer does not match the 118 // one we dirtied. 119 if r1 != r2 { 120 continue 121 } 122 123 // Finally, verify that the item has been properly cleaned. 124 isCleanGeneric(t, r2) 125 126 return 127 } 128 129 t.Fatalf("original item not found") 130 } 131 132 func takeGeneric(t *testing.T, p interface{}) pool.Recycler { 133 t.Helper() 134 135 switch pp := p.(type) { 136 case *pool.WriteBuffer: 137 return pp.Take() 138 139 case *pool.ReadBuffer: 140 return pp.Take() 141 142 default: 143 t.Fatalf("unknown pool type: %T", p) 144 } 145 146 return nil 147 } 148 149 func returnGeneric(t *testing.T, p, item interface{}) { 150 t.Helper() 151 152 switch pp := p.(type) { 153 case *pool.WriteBuffer: 154 pp.Return(item.(*buffer.Write)) 155 156 case *pool.ReadBuffer: 157 pp.Return(item.(*buffer.Read)) 158 159 default: 160 t.Fatalf("unknown pool type: %T", p) 161 } 162 } 163 164 func dirtyGeneric(t *testing.T, i interface{}) { 165 t.Helper() 166 167 switch item := i.(type) { 168 case *mockRecycler: 169 *item = true 170 171 case *buffer.Write: 172 dirtySlice(item[:]) 173 174 case *buffer.Read: 175 dirtySlice(item[:]) 176 177 default: 178 t.Fatalf("unknown item type: %T", i) 179 } 180 181 } 182 183 func dirtySlice(slice []byte) { 184 for i := range slice { 185 slice[i] = 0xff 186 } 187 } 188 189 func isCleanGeneric(t *testing.T, i interface{}) { 190 t.Helper() 191 192 switch item := i.(type) { 193 case *mockRecycler: 194 if isDirty := *item; isDirty { 195 t.Fatalf("mock recycler still diry") 196 } 197 198 case *buffer.Write: 199 isCleanSlice(t, item[:]) 200 201 case *buffer.Read: 202 isCleanSlice(t, item[:]) 203 204 default: 205 t.Fatalf("unknown item type: %T", i) 206 } 207 } 208 209 func isCleanSlice(t *testing.T, slice []byte) { 210 t.Helper() 211 212 expSlice := make([]byte, len(slice)) 213 if !bytes.Equal(expSlice, slice) { 214 t.Fatalf("slice not recycled, want: %v, got: %v", 215 expSlice, slice) 216 } 217 }