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  }