github.com/koko1123/flow-go-1@v0.29.6/module/mempool/stdmap/incorporated_result_seals_test.go (about)

     1  package stdmap
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"pgregory.net/rapid"
    10  
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/utils/unittest"
    13  )
    14  
    15  // icrSealsMachine is a description of a state machine for testing IncorporatedresultSeals
    16  type icrSealsMachine struct {
    17  	icrs  *IncorporatedResultSeals       // icrSeals being tested
    18  	state []*flow.IncorporatedResultSeal // model of the icrSeals
    19  }
    20  
    21  // Init is an action for initializing a icrSeals instance.
    22  func (m *icrSealsMachine) Init(t *rapid.T) {
    23  	m.icrs = NewIncorporatedResultSeals(1000)
    24  }
    25  
    26  // Add is a conditional action which adds an item to the icrSeals.
    27  func (m *icrSealsMachine) Add(t *rapid.T) {
    28  	i := rapid.Uint64().Draw(t, "i").(uint64)
    29  
    30  	seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) {
    31  		s.Header.Height = i
    32  	})
    33  
    34  	_, err := m.icrs.Add(seal)
    35  	require.NoError(t, err)
    36  
    37  	// we do not re-add already present seals
    38  	unmet := true
    39  	for _, v := range m.state {
    40  		if v.ID() == seal.ID() {
    41  			unmet = false
    42  		}
    43  	}
    44  
    45  	if unmet && seal.Header.Height >= m.icrs.lowestHeight {
    46  		m.state = append(m.state, seal)
    47  	}
    48  }
    49  
    50  // Prune is a Conditional action that removes elements of height strictly lower than its argument
    51  func (m *icrSealsMachine) PruneUpToHeight(t *rapid.T) {
    52  	h := rapid.Uint64().Draw(t, "h").(uint64)
    53  	err := m.icrs.PruneUpToHeight(h)
    54  	if h >= m.icrs.lowestHeight {
    55  		require.NoError(t, err)
    56  		assert.Equal(t, m.icrs.lowestHeight, h)
    57  	}
    58  
    59  	filtered_state := make([]*flow.IncorporatedResultSeal, 0)
    60  	for _, v := range m.state {
    61  		if v.Header.Height >= h {
    62  			filtered_state = append(filtered_state, v)
    63  		}
    64  	}
    65  	m.state = filtered_state
    66  }
    67  
    68  // Get is an action that retrieves an element from the icrSeals
    69  func (m *icrSealsMachine) Get(t *rapid.T) {
    70  	n := len(m.state)
    71  	// skip if the store is empty
    72  	if n == 0 {
    73  		return
    74  	}
    75  	i := rapid.IntRange(0, n-1).Draw(t, "i").(int)
    76  
    77  	s := m.state[i]
    78  	actual, ok := m.icrs.ByID(s.ID())
    79  	require.True(t, ok)
    80  	require.Equal(t, s, actual)
    81  
    82  }
    83  
    84  // GetUnknown is an action that removes an unknown element from the icrSeals
    85  // This mostly tests ByID has no insertion side-effects
    86  func (m *icrSealsMachine) GetUnknown(t *rapid.T) {
    87  	n := len(m.state)
    88  	// skip if the store is empty
    89  	if n == 0 {
    90  		return
    91  	}
    92  	i := rapid.IntRange(0, n-1).Draw(t, "i").(int)
    93  	seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) {
    94  		s.Header.Height = uint64(i)
    95  	})
    96  
    97  	// check seal is unknown
    98  	unknown := true
    99  	for _, v := range m.state {
   100  		if v.ID() == seal.ID() {
   101  			unknown = false
   102  		}
   103  	}
   104  
   105  	if unknown {
   106  		_, found := m.icrs.ByID(seal.ID())
   107  		require.False(t, found)
   108  	}
   109  	// no modification of state
   110  
   111  }
   112  
   113  // Remove is a conditional action that removes a known element from the icrSeals
   114  func (m *icrSealsMachine) Remove(t *rapid.T) {
   115  	n := len(m.state)
   116  	// skip if the store is empty
   117  	if n == 0 {
   118  		return
   119  	}
   120  	i := rapid.IntRange(0, n-1).Draw(t, "i").(int)
   121  
   122  	s := m.state[i]
   123  	ok := m.icrs.Remove(s.ID())
   124  	require.True(t, ok)
   125  
   126  	// remove m[i], we don't care about ordering here
   127  	m.state[n-1], m.state[i] = m.state[i], m.state[n-1]
   128  	m.state = m.state[:n-1]
   129  
   130  }
   131  
   132  // RemoveUnknown is an action that removes an unknown element from the icrSeals
   133  // This mostly tests Remove has no insertion side-effects
   134  func (m *icrSealsMachine) RemoveUnknown(t *rapid.T) {
   135  	n := len(m.state)
   136  	// skip if the store is empty
   137  	if n == 0 {
   138  		return
   139  	}
   140  	i := rapid.IntRange(0, n-1).Draw(t, "i").(int)
   141  	seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) {
   142  		s.Header.Height = uint64(i)
   143  	})
   144  
   145  	// check seal is unknown
   146  	unknown := true
   147  	for _, v := range m.state {
   148  		if v.ID() == seal.ID() {
   149  			unknown = false
   150  		}
   151  	}
   152  
   153  	if unknown {
   154  		removed := m.icrs.Remove(seal.ID())
   155  		require.False(t, removed)
   156  	}
   157  	// no modification of state
   158  
   159  }
   160  
   161  // Check runs after every action and verifies that all required invariants hold.
   162  func (m *icrSealsMachine) Check(t *rapid.T) {
   163  	if int(m.icrs.Size()) != len(m.state) {
   164  		t.Fatalf("store size mismatch: %v vs expected %v", m.icrs.Size(), len(m.state))
   165  	}
   166  	assert.ElementsMatch(t, m.icrs.All(), m.state)
   167  }
   168  
   169  // Run the icrSeals state machine and test it against its model
   170  func TestIcrs(t *testing.T) {
   171  	rapid.Check(t, rapid.Run(&icrSealsMachine{}))
   172  }
   173  
   174  func TestIncorporatedResultSeals(t *testing.T) {
   175  	t.Parallel()
   176  
   177  	// after adding one receipt, should be able to query it back by previous result id
   178  	// after removing, should not be able to query it back.
   179  	t.Run("add remove get", func(t *testing.T) {
   180  		pool := NewIncorporatedResultSeals(1000)
   181  
   182  		seal := unittest.IncorporatedResultSeal.Fixture()
   183  
   184  		ok, err := pool.Add(seal)
   185  		require.NoError(t, err)
   186  		require.True(t, ok)
   187  
   188  		actual, ok := pool.ByID(seal.ID())
   189  		require.True(t, ok)
   190  		require.Equal(t, seal, actual)
   191  
   192  		deleted := pool.Remove(seal.ID())
   193  		require.True(t, deleted)
   194  
   195  		_, ok = pool.ByID(seal.ID())
   196  		require.False(t, ok)
   197  	})
   198  
   199  	t.Run("add 100 prune by height", func(t *testing.T) {
   200  		pool := NewIncorporatedResultSeals(1000)
   201  
   202  		seals := make([]*flow.IncorporatedResultSeal, 0, 100)
   203  		for i := 0; i < 100; i++ {
   204  			seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) {
   205  				s.Header.Height = uint64(i)
   206  			})
   207  
   208  			seals = append(seals, seal)
   209  		}
   210  
   211  		for _, seal := range seals {
   212  			ok, err := pool.Add(seal)
   213  			require.NoError(t, err)
   214  			require.True(t, ok, "seal at height %d was not added", seal.Header.Height)
   215  		}
   216  		verifyPresent(t, pool, seals...)
   217  
   218  		err := pool.PruneUpToHeight(5)
   219  		require.NoError(t, err)
   220  		verifyAbsent(t, pool, seals[:5]...)
   221  		verifyPresent(t, pool, seals[5:]...)
   222  
   223  		err = pool.PruneUpToHeight(10)
   224  		require.NoError(t, err)
   225  		verifyAbsent(t, pool, seals[:10]...)
   226  		verifyPresent(t, pool, seals[10:]...)
   227  	})
   228  
   229  	t.Run("prune with some nonexisting heights", func(t *testing.T) {
   230  		pool := NewIncorporatedResultSeals(1000)
   231  
   232  		// create seals, but NOT for heights 2 and 3
   233  		seals := make([]*flow.IncorporatedResultSeal, 0, 6)
   234  		for _, h := range []uint64{0, 1, 4, 5, 6, 7} {
   235  			seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) {
   236  				s.Header.Height = h
   237  			})
   238  			seals = append(seals, seal)
   239  			ok, err := pool.Add(seal)
   240  			require.NoError(t, err)
   241  			require.True(t, ok)
   242  		}
   243  
   244  		err := pool.PruneUpToHeight(5)
   245  		require.NoError(t, err)
   246  		verifyAbsent(t, pool, seals[:3]...)
   247  		verifyPresent(t, pool, seals[3:]...)
   248  	})
   249  }
   250  
   251  func verifyPresent(t *testing.T, pool *IncorporatedResultSeals, seals ...*flow.IncorporatedResultSeal) {
   252  	for _, seal := range seals {
   253  		_, ok := pool.ByID(seal.ID())
   254  		require.True(t, ok, "seal at height %d should be in mempool", seal.Header.Height)
   255  	}
   256  }
   257  
   258  func verifyAbsent(t *testing.T, pool *IncorporatedResultSeals, seals ...*flow.IncorporatedResultSeal) {
   259  	for _, seal := range seals {
   260  		_, ok := pool.ByID(seal.ID())
   261  		require.False(t, ok, "seal at height %d should not be in mempool", seal.Header.Height)
   262  	}
   263  }