gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/miner/splitsetheap_test.go (about) 1 package miner 2 3 import ( 4 "math/rand" 5 "testing" 6 7 "gitlab.com/NebulousLabs/fastrand" 8 "gitlab.com/SiaPrime/SiaPrime/types" 9 ) 10 11 // TestMapHeapSimple test max-heap and min-heap versions of the MapHeap on the 12 // same sequence of pushes and pops. The pushes are done in increasing value of 13 // averageFee (the value by which elements are compared). 14 func TestMapHeapSimple(t *testing.T) { 15 max := &mapHeap{ 16 selectID: make(map[splitSetID]*mapElement), 17 data: nil, 18 size: 0, 19 minHeap: false, 20 } 21 min := &mapHeap{ 22 selectID: make(map[splitSetID]*mapElement), 23 data: nil, 24 size: 0, 25 minHeap: true, 26 } 27 max.init() 28 min.init() 29 30 randSlice := fastrand.Perm(1000) 31 for _, i := range randSlice { 32 e1 := &mapElement{ 33 set: &splitSet{ 34 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 35 size: uint64(10 * i), 36 transactions: make([]types.Transaction, 0), 37 }, 38 39 id: splitSetID(i), 40 index: 0, 41 } 42 e2 := &mapElement{ 43 set: &splitSet{ 44 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 45 size: uint64(10 * i), 46 transactions: make([]types.Transaction, 0), 47 }, 48 49 id: splitSetID(i), 50 index: 0, 51 } 52 max.push(e1) 53 min.push(e2) 54 } 55 56 for i := 0; i < 1000; i++ { 57 maxPop := max.pop() 58 minPop := min.pop() 59 60 if int(maxPop.id) != 999-i { 61 t.Error("Unexpected splitSetID in result from max-heap pop.") 62 } 63 if int(minPop.id) != i { 64 t.Error("Unexpected splitSetID in result from min-heap pop.") 65 } 66 if maxPop.set.averageFee.Cmp(types.SiacoinPrecision.Mul64(uint64(999-i))) != 0 { 67 t.Error("Unexpected currency value in result from max-heap pop.") 68 } 69 if minPop.set.averageFee.Cmp(types.SiacoinPrecision.Mul64(uint64(i))) != 0 { 70 t.Error("Unexpected currency value in result from min-heap pop.") 71 } 72 } 73 } 74 75 // TestMapHeapSize tests that the size of MapHeaps changes accordingly with the 76 // sizes of elements added to it, and with those elements removed from it. Tests 77 // a max-heap and min-heap on the same sequence of pushes and pops. 78 func TestMapHeapSize(t *testing.T) { 79 max := &mapHeap{ 80 selectID: make(map[splitSetID]*mapElement), 81 data: nil, 82 size: 0, 83 minHeap: false, 84 } 85 min := &mapHeap{ 86 selectID: make(map[splitSetID]*mapElement), 87 data: nil, 88 size: 0, 89 minHeap: true, 90 } 91 max.init() 92 min.init() 93 94 var expectedSize uint64 95 96 randSlice := fastrand.Perm(1000) 97 for _, i := range randSlice { 98 e1 := &mapElement{ 99 set: &splitSet{ 100 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 101 size: uint64(100 * i), 102 transactions: make([]types.Transaction, 0), 103 }, 104 105 id: splitSetID(i), 106 index: 0, 107 } 108 e2 := &mapElement{ 109 set: &splitSet{ 110 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 111 size: uint64(100 * i), 112 transactions: make([]types.Transaction, 0), 113 }, 114 115 id: splitSetID(i), 116 index: 0, 117 } 118 max.push(e1) 119 min.push(e2) 120 expectedSize += e1.set.size 121 } 122 123 if max.size != expectedSize { 124 t.Error("Max-heap size different than expected size.") 125 } 126 if min.size != expectedSize { 127 t.Error("Min-heap size different than expected size.") 128 } 129 130 for i := 0; i < 1000; i++ { 131 maxPop := max.pop() 132 minPop := min.pop() 133 134 if maxPop.set.size != uint64(100*(999-i)) { 135 t.Error("Unexpected set size in result from max-heap pop.") 136 } 137 if minPop.set.size != uint64(100*i) { 138 t.Error("Unexpected set size in result from min-heap pop.") 139 } 140 141 } 142 } 143 144 // TestMapHeapRemoveBySetID pushes a sequence of elements onto a max-heap and 145 // min-heap. Then it removes a random element using its splitSetID, and checks 146 // that it has been removed. 147 func TestMapHeapRemoveBySetID(t *testing.T) { 148 max := &mapHeap{ 149 selectID: make(map[splitSetID]*mapElement), 150 data: nil, 151 size: 0, 152 minHeap: false, 153 } 154 min := &mapHeap{ 155 selectID: make(map[splitSetID]*mapElement), 156 data: nil, 157 size: 0, 158 minHeap: true, 159 } 160 max.init() 161 min.init() 162 163 for i := 0; i < 5000; i++ { 164 e1 := &mapElement{ 165 set: &splitSet{ 166 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 167 size: uint64(10 * i), 168 transactions: make([]types.Transaction, 0), 169 }, 170 171 id: splitSetID(i), 172 index: 0, 173 } 174 e2 := &mapElement{ 175 set: &splitSet{ 176 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 177 size: uint64(10 * i), 178 transactions: make([]types.Transaction, 0), 179 }, 180 181 id: splitSetID(i), 182 index: 0, 183 } 184 max.push(e1) 185 min.push(e2) 186 } 187 188 randID := splitSetID(rand.Intn(5000)) 189 firstToBeRemoved := max.selectID[randID] 190 191 // Iterate over data in min heap and max heap to confirm the element to be 192 // removed is actually there. 193 inMaxHeap := false 194 inMinHeap := false 195 for _, v := range max.data { 196 if v.id == firstToBeRemoved.id { 197 inMaxHeap = true 198 break 199 } 200 } 201 for _, v := range min.data { 202 if v.id == firstToBeRemoved.id { 203 inMinHeap = true 204 break 205 } 206 } 207 208 if !inMinHeap || !inMaxHeap { 209 t.Error("Element not found in heap(s) before being removed by splitSetID.") 210 } 211 if max.selectID[randID] == nil || min.selectID[randID] == nil { 212 t.Error("Element not found in map(s) before being removed by splitSetID") 213 } 214 215 minSizeBefore := min.size 216 maxSizeBefore := max.size 217 minRemovedSetSize := min.selectID[randID].set.size 218 maxRemovedSetSize := max.selectID[randID].set.size 219 220 max.removeSetByID(randID) 221 min.removeSetByID(randID) 222 minSizeAfter := min.size 223 maxSizeAfter := max.size 224 if minSizeBefore-minRemovedSetSize != minSizeAfter { 225 t.Error("unexpected difference in size after removing from min heap.") 226 } 227 if maxSizeBefore-maxRemovedSetSize != maxSizeAfter { 228 t.Error("unexpected difference in size after removing from max heap.") 229 } 230 231 // Iterate over data in min heap and max heap to confirm the element to be 232 // removed was actually removed 233 removedFromMax := true 234 removedFromMin := true 235 for _, v := range max.data { 236 if v.id == firstToBeRemoved.id { 237 removedFromMax = false 238 break 239 } 240 } 241 for _, v := range min.data { 242 if v.id == firstToBeRemoved.id { 243 removedFromMin = false 244 break 245 } 246 } 247 if !removedFromMin { 248 t.Error("Element found in min heap(s) after being removed by splitSetID.") 249 } 250 if !removedFromMax { 251 t.Error("Element found in max heap(s) after being removed by splitSetID.") 252 } 253 _, inMinMap := min.selectID[randID] 254 _, inMaxMap := max.selectID[randID] 255 if inMinMap { 256 t.Error("Element found in min map(s) after being removed by splitSetID") 257 } 258 if inMaxMap { 259 t.Error("Element found in max map(s) after being removed by splitSetID") 260 } 261 } 262 263 // TestMapHeapPeek test the Peek method. First, on an empty heap Peek should 264 // return false. Then it checks that Peek returns the same result as the next 265 // Pop. 266 func TestMapHeapPeek(t *testing.T) { 267 max := &mapHeap{ 268 selectID: make(map[splitSetID]*mapElement), 269 data: nil, 270 size: 0, 271 minHeap: false, 272 } 273 min := &mapHeap{ 274 selectID: make(map[splitSetID]*mapElement), 275 data: nil, 276 size: 0, 277 minHeap: true, 278 } 279 max.init() 280 min.init() 281 282 minSizeBefore := min.size 283 maxSizeBefore := max.size 284 285 _, maxNotEmpty := max.peek() 286 _, minNotEmpty := min.peek() 287 minSizeAfter := min.size 288 maxSizeAfter := max.size 289 if maxNotEmpty { 290 t.Error("Unexpected result from max.Peek(), heap not empty") 291 } 292 if minNotEmpty { 293 t.Error("Unexpected result from max.Peek(), heap not empty") 294 } 295 if minSizeBefore != minSizeAfter || maxSizeBefore != maxSizeAfter { 296 t.Error("expected heap size not to change from peek.") 297 } 298 299 for i := 0; i < 10; i++ { 300 e1 := &mapElement{ 301 set: &splitSet{ 302 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 303 size: uint64(10 * i), 304 transactions: make([]types.Transaction, 0), 305 }, 306 307 id: splitSetID(i), 308 index: 0, 309 } 310 e2 := &mapElement{ 311 set: &splitSet{ 312 averageFee: types.SiacoinPrecision.Mul64(uint64(i)), 313 size: uint64(10 * i), 314 transactions: make([]types.Transaction, 0), 315 }, 316 317 id: splitSetID(i), 318 index: 0, 319 } 320 max.push(e1) 321 min.push(e2) 322 } 323 324 for i := 0; i < 10; i++ { 325 minSizeBefore := min.size 326 maxSizeBefore := max.size 327 328 maxPeek, maxNotEmpty := max.peek() 329 minPeek, minNotEmpty := min.peek() 330 minSizeAfter := min.size 331 maxSizeAfter := max.size 332 if minSizeBefore != minSizeAfter || maxSizeBefore != maxSizeAfter { 333 t.Error("expected heap size not to change from peek.") 334 } 335 if !maxNotEmpty { 336 t.Error("Unexpected result from max.Peek(), heap empty after pushes") 337 } 338 if !minNotEmpty { 339 t.Error("Unexpected result from max.Peek(), heap empty after pushes") 340 } 341 342 maxPop := max.pop() 343 minPop := min.pop() 344 if int(maxPop.id) != int(maxPeek.id) { 345 t.Error("Unexpected splitSetID in result from max-heap Peek.") 346 } 347 if int(minPop.id) != int(minPeek.id) { 348 t.Error("Unexpected splitSetID in result from min-heap Peek.") 349 } 350 if maxPop.set.averageFee.Cmp(maxPeek.set.averageFee) != 0 { 351 t.Error("Unexpected currency value in result from max-heap Peek.") 352 } 353 if minPop.set.averageFee.Cmp(minPeek.set.averageFee) != 0 { 354 t.Error("Unexpected currency value in result from min-heap Peek.") 355 } 356 } 357 }