github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/bitfield_queue_test.go (about)

     1  package miner_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/filecoin-project/go-address"
     7  	"github.com/filecoin-project/go-bitfield"
     8  	"github.com/filecoin-project/go-state-types/abi"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/miner"
    13  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    14  	"github.com/filecoin-project/specs-actors/v4/support/mock"
    15  )
    16  
    17  const testAmtBitwidth = 3
    18  
    19  func TestBitfieldQueue(t *testing.T) {
    20  	t.Run("adds values to empty queue", func(t *testing.T) {
    21  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
    22  
    23  		values := []uint64{1, 2, 3, 4}
    24  		epoch := abi.ChainEpoch(42)
    25  		require.NoError(t, queue.AddToQueueValues(epoch, values...))
    26  
    27  		ExpectBQ().
    28  			Add(epoch, values...).
    29  			Equals(t, queue)
    30  	})
    31  
    32  	t.Run("adds bitfield to empty queue", func(t *testing.T) {
    33  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
    34  
    35  		values := []uint64{1, 2, 3, 4}
    36  		epoch := abi.ChainEpoch(42)
    37  
    38  		require.NoError(t, queue.AddToQueue(epoch, bitfield.NewFromSet(values)))
    39  
    40  		ExpectBQ().
    41  			Add(epoch, values...).
    42  			Equals(t, queue)
    43  	})
    44  
    45  	t.Run("quantizes added epochs according to quantization spec", func(t *testing.T) {
    46  		queue := emptyBitfieldQueueWithQuantizing(t, miner.NewQuantSpec(5, 3), testAmtBitwidth)
    47  
    48  		for _, val := range []uint64{0, 2, 3, 4, 7, 8, 9} {
    49  			require.NoError(t, queue.AddToQueueValues(abi.ChainEpoch(val), val))
    50  		}
    51  
    52  		// expect values to only be set on quantization boundaries
    53  		ExpectBQ().
    54  			Add(abi.ChainEpoch(3), 0, 2, 3).
    55  			Add(abi.ChainEpoch(8), 4, 7, 8).
    56  			Add(abi.ChainEpoch(13), 9).
    57  			Equals(t, queue)
    58  	})
    59  
    60  	t.Run("quantizes added epochs according to quantization spec", func(t *testing.T) {
    61  		queue := emptyBitfieldQueueWithQuantizing(t, miner.NewQuantSpec(5, 3), testAmtBitwidth)
    62  
    63  		for _, val := range []uint64{0, 2, 3, 4, 7, 8, 9} {
    64  			err := queue.AddToQueueValues(abi.ChainEpoch(val), val)
    65  			require.NoError(t, err)
    66  		}
    67  
    68  		// expect values to only be set on quantization boundaries
    69  		ExpectBQ().
    70  			Add(abi.ChainEpoch(3), 0, 2, 3).
    71  			Add(abi.ChainEpoch(8), 4, 7, 8).
    72  			Add(abi.ChainEpoch(13), 9).
    73  			Equals(t, queue)
    74  	})
    75  
    76  	t.Run("merges values withing same epoch", func(t *testing.T) {
    77  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
    78  
    79  		epoch := abi.ChainEpoch(42)
    80  
    81  		require.NoError(t, queue.AddToQueueValues(epoch, 1, 3))
    82  		require.NoError(t, queue.AddToQueueValues(epoch, 2, 4))
    83  
    84  		ExpectBQ().
    85  			Add(epoch, 1, 2, 3, 4).
    86  			Equals(t, queue)
    87  	})
    88  
    89  	t.Run("adds values to different epochs", func(t *testing.T) {
    90  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
    91  
    92  		epoch1 := abi.ChainEpoch(42)
    93  		epoch2 := abi.ChainEpoch(93)
    94  
    95  		require.NoError(t, queue.AddToQueueValues(epoch1, 1, 3))
    96  		require.NoError(t, queue.AddToQueueValues(epoch2, 2, 4))
    97  
    98  		ExpectBQ().
    99  			Add(epoch1, 1, 3).
   100  			Add(epoch2, 2, 4).
   101  			Equals(t, queue)
   102  	})
   103  
   104  	t.Run("PouUntil from empty queue returns empty bitfield", func(t *testing.T) {
   105  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
   106  
   107  		// TODO: broken pending https://github.com/filecoin-project/go-amt-ipld/issues/18
   108  		//emptyQueue, err := queue.Root()
   109  		//require.NoError(t, err)
   110  
   111  		next, modified, err := queue.PopUntil(42)
   112  		require.NoError(t, err)
   113  		assert.False(t, modified)
   114  
   115  		// no values are returned
   116  		count, err := next.Count()
   117  		require.NoError(t, err)
   118  		assert.Equal(t, 0, int(count))
   119  
   120  		// queue is still empty
   121  		//root, err := queue.Root()
   122  		//assert.Equal(t, emptyQueue, root)
   123  	})
   124  
   125  	t.Run("PopUntil does nothing if 'until' parameter before first value", func(t *testing.T) {
   126  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
   127  
   128  		epoch1 := abi.ChainEpoch(42)
   129  		epoch2 := abi.ChainEpoch(93)
   130  
   131  		require.NoError(t, queue.AddToQueueValues(epoch1, 1, 3))
   132  		require.NoError(t, queue.AddToQueueValues(epoch2, 2, 4))
   133  
   134  		next, modified, err := queue.PopUntil(epoch1 - 1)
   135  		assert.False(t, modified)
   136  		require.NoError(t, err)
   137  		assert.False(t, modified)
   138  
   139  		// no values are returned
   140  		count, err := next.Count()
   141  		require.NoError(t, err)
   142  		assert.Equal(t, 0, int(count))
   143  
   144  		// queue remains the same
   145  		ExpectBQ().
   146  			Add(epoch1, 1, 3).
   147  			Add(epoch2, 2, 4).
   148  			Equals(t, queue)
   149  	})
   150  
   151  	t.Run("PopUntil removes and returns entries before and including target epoch", func(t *testing.T) {
   152  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
   153  
   154  		epoch1 := abi.ChainEpoch(42)
   155  		epoch2 := abi.ChainEpoch(93)
   156  		epoch3 := abi.ChainEpoch(94)
   157  		epoch4 := abi.ChainEpoch(203)
   158  
   159  		require.NoError(t, queue.AddToQueueValues(epoch1, 1, 3))
   160  		require.NoError(t, queue.AddToQueueValues(epoch2, 5))
   161  		require.NoError(t, queue.AddToQueueValues(epoch3, 6, 7, 8))
   162  		require.NoError(t, queue.AddToQueueValues(epoch4, 2, 4))
   163  
   164  		// Required to ensure queue is in a sane state for PopUntil
   165  		_, err := queue.Root()
   166  		require.NoError(t, err)
   167  
   168  		next, modified, err := queue.PopUntil(epoch2)
   169  		require.NoError(t, err)
   170  		// modified should be true to indicate queue has changed
   171  		assert.True(t, modified)
   172  
   173  		// values from first two epochs are returned
   174  		assertBitfieldEquals(t, next, 1, 3, 5)
   175  
   176  		// queue only contains remaining values
   177  		ExpectBQ().
   178  			Add(epoch3, 6, 7, 8).
   179  			Add(epoch4, 2, 4).
   180  			Equals(t, queue)
   181  
   182  		// subsequent call to epoch less than next does nothing.
   183  		next, modified, err = queue.PopUntil(epoch3 - 1)
   184  		require.NoError(t, err)
   185  		assert.False(t, modified)
   186  
   187  		// no values are returned
   188  		assertBitfieldEquals(t, next, []uint64{}...)
   189  
   190  		// queue only contains remaining values
   191  		ExpectBQ().
   192  			Add(epoch3, 6, 7, 8).
   193  			Add(epoch4, 2, 4).
   194  			Equals(t, queue)
   195  
   196  		// popping the rest of the queue gets the rest of the values
   197  		next, modified, err = queue.PopUntil(epoch4)
   198  		require.NoError(t, err)
   199  		assert.True(t, modified)
   200  
   201  		// rest of values are returned
   202  		assertBitfieldEquals(t, next, 2, 4, 6, 7, 8)
   203  
   204  		// queue is now empty
   205  		ExpectBQ().
   206  			Equals(t, queue)
   207  	})
   208  
   209  	t.Run("cuts elements", func(t *testing.T) {
   210  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
   211  
   212  		epoch1 := abi.ChainEpoch(42)
   213  		epoch2 := abi.ChainEpoch(93)
   214  
   215  		require.NoError(t, queue.AddToQueueValues(epoch1, 1, 2, 3, 4, 99))
   216  		require.NoError(t, queue.AddToQueueValues(epoch2, 5, 6))
   217  
   218  		require.NoError(t, queue.Cut(bitfield.NewFromSet([]uint64{2, 4, 5, 6})))
   219  
   220  		ExpectBQ().
   221  			Add(epoch1, 1, 2, 95). // 3 shifts down to 2, 99 down to 95
   222  			Equals(t, queue)
   223  	})
   224  
   225  	t.Run("adds empty bitfield to queue", func(t *testing.T) {
   226  		queue := emptyBitfieldQueue(t, testAmtBitwidth)
   227  
   228  		epoch := abi.ChainEpoch(42)
   229  		require.NoError(t, queue.AddToQueue(epoch, bf()))
   230  
   231  		// ensures we don't add an empty entry.
   232  		ExpectBQ().Equals(t, queue)
   233  	})
   234  
   235  }
   236  
   237  func emptyBitfieldQueueWithQuantizing(t *testing.T, quant miner.QuantSpec, bitwidth int) miner.BitfieldQueue {
   238  	rt := mock.NewBuilder(address.Undef).Build(t)
   239  	store := adt.AsStore(rt)
   240  	emptyArray, err := adt.StoreEmptyArray(store, bitwidth)
   241  	require.NoError(t, err)
   242  
   243  	queue, err := miner.LoadBitfieldQueue(store, emptyArray, quant, bitwidth)
   244  	require.NoError(t, err)
   245  	return queue
   246  }
   247  
   248  func emptyBitfieldQueue(t *testing.T, bitwidth int) miner.BitfieldQueue {
   249  	return emptyBitfieldQueueWithQuantizing(t, miner.NoQuantization, bitwidth)
   250  }
   251  
   252  type bqExpectation struct {
   253  	expected map[abi.ChainEpoch][]uint64
   254  }
   255  
   256  func ExpectBQ() *bqExpectation {
   257  	return &bqExpectation{expected: make(map[abi.ChainEpoch][]uint64)}
   258  }
   259  
   260  func (bqe *bqExpectation) Add(epoch abi.ChainEpoch, values ...uint64) *bqExpectation {
   261  	bqe.expected[epoch] = values
   262  	return bqe
   263  }
   264  
   265  func (bqe *bqExpectation) Equals(t *testing.T, q miner.BitfieldQueue) {
   266  	// ensure cached changes are ready to be iterated
   267  	_, err := q.Root()
   268  	require.NoError(t, err)
   269  
   270  	assert.Equal(t, uint64(len(bqe.expected)), q.Length())
   271  
   272  	err = q.ForEach(func(epoch abi.ChainEpoch, bf bitfield.BitField) error {
   273  		values, ok := bqe.expected[epoch]
   274  		require.True(t, ok)
   275  
   276  		assertBitfieldEquals(t, bf, values...)
   277  		return nil
   278  	})
   279  	require.NoError(t, err)
   280  }