github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/mempool/queue/queue_test.go (about)

     1  package queue
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/onflow/flow-go/module/mempool/entity"
    12  	"github.com/onflow/flow-go/utils/unittest"
    13  )
    14  
    15  func TestQueue(t *testing.T) {
    16  
    17  	/* Input queue:
    18  	  g-b
    19  	     \
    20  	f--d--c-a
    21  	  /
    22  	 e
    23  
    24  	*/
    25  
    26  	a := unittest.ExecutableBlockFixture(nil, nil)
    27  	c := unittest.ExecutableBlockFixtureWithParent(nil, a.Block.Header, nil)
    28  	b := unittest.ExecutableBlockFixtureWithParent(nil, c.Block.Header, nil)
    29  	d := unittest.ExecutableBlockFixtureWithParent(nil, c.Block.Header, nil)
    30  	e := unittest.ExecutableBlockFixtureWithParent(nil, d.Block.Header, nil)
    31  	f := unittest.ExecutableBlockFixtureWithParent(nil, d.Block.Header, nil)
    32  	g := unittest.ExecutableBlockFixtureWithParent(nil, b.Block.Header, nil)
    33  
    34  	dBroken := unittest.ExecutableBlockFixtureWithParent(nil, c.Block.Header, nil)
    35  	dBroken.Block.Header.Height += 2 //change height
    36  
    37  	queue := NewQueue(a)
    38  
    39  	t.Run("Adding", func(t *testing.T) {
    40  		stored, _ := queue.TryAdd(b) //parent not stored yet
    41  		size := queue.Size()
    42  		height := queue.Height()
    43  		assert.False(t, stored)
    44  		assert.Equal(t, 1, size)
    45  		assert.Equal(t, uint64(0), height)
    46  
    47  		stored, new := queue.TryAdd(c)
    48  		size = queue.Size()
    49  		height = queue.Height()
    50  		assert.True(t, stored)
    51  		assert.True(t, new)
    52  		assert.Equal(t, 2, size)
    53  		assert.Equal(t, uint64(1), height)
    54  
    55  		stored, new = queue.TryAdd(b)
    56  		size = queue.Size()
    57  		height = queue.Height()
    58  		assert.True(t, stored)
    59  		assert.True(t, new)
    60  		assert.Equal(t, 3, size)
    61  		assert.Equal(t, uint64(2), height)
    62  
    63  		stored, new = queue.TryAdd(b) //repeat
    64  		size = queue.Size()
    65  		height = queue.Height()
    66  		assert.True(t, stored)
    67  		assert.False(t, new)
    68  		assert.Equal(t, 3, size)
    69  		assert.Equal(t, uint64(2), height)
    70  
    71  		stored, _ = queue.TryAdd(f) //parent not stored yet
    72  		assert.False(t, stored)
    73  
    74  		stored, new = queue.TryAdd(d)
    75  		size = queue.Size()
    76  		height = queue.Height()
    77  		assert.True(t, stored)
    78  		assert.True(t, new)
    79  		assert.Equal(t, 4, size)
    80  		assert.Equal(t, uint64(2), height)
    81  
    82  		stored, _ = queue.TryAdd(dBroken) // wrong height
    83  		assert.False(t, stored)
    84  
    85  		stored, new = queue.TryAdd(e)
    86  		size = queue.Size()
    87  		height = queue.Height()
    88  		assert.True(t, stored)
    89  		assert.True(t, new)
    90  		assert.Equal(t, 5, size)
    91  		assert.Equal(t, uint64(3), height)
    92  
    93  		stored, new = queue.TryAdd(f)
    94  		size = queue.Size()
    95  		height = queue.Height()
    96  		assert.True(t, stored)
    97  		assert.True(t, new)
    98  		assert.Equal(t, 6, size)
    99  		assert.Equal(t, uint64(3), height)
   100  
   101  		stored, new = queue.TryAdd(g)
   102  		size = queue.Size()
   103  		height = queue.Height()
   104  		assert.True(t, stored)
   105  		assert.True(t, new)
   106  		assert.Equal(t, 7, size)
   107  		assert.Equal(t, uint64(3), height)
   108  	})
   109  
   110  	t.Run("Dismounting", func(t *testing.T) {
   111  		// dismount queue
   112  		blockA, queuesA := queue.Dismount()
   113  		assert.Equal(t, a, blockA)
   114  		require.Len(t, queuesA, 1)
   115  		assert.Equal(t, 6, queuesA[0].Size())
   116  		assert.Equal(t, uint64(2), queuesA[0].Height())
   117  
   118  		blockC, queuesC := queuesA[0].Dismount()
   119  		assert.Equal(t, c, blockC)
   120  		require.Len(t, queuesC, 2)
   121  
   122  		// order of children is not guaranteed
   123  		var queueD *Queue
   124  		var queueB *Queue
   125  		if queuesC[0].Head.Item == d {
   126  			queueD = queuesC[0]
   127  			queueB = queuesC[1]
   128  		} else {
   129  			queueD = queuesC[1]
   130  			queueB = queuesC[0]
   131  		}
   132  		assert.Equal(t, d, queueD.Head.Item)
   133  		sizeD := queueD.Size()
   134  		heightD := queueD.Height()
   135  		sizeB := queueB.Size()
   136  		heightB := queueB.Height()
   137  
   138  		assert.Equal(t, 3, sizeD)
   139  		assert.Equal(t, uint64(1), heightD)
   140  		assert.Equal(t, 2, sizeB)
   141  		assert.Equal(t, uint64(1), heightB)
   142  
   143  		blockD, queuesD := queueD.Dismount()
   144  		assert.Equal(t, d, blockD)
   145  		assert.Len(t, queuesD, 2)
   146  	})
   147  
   148  	t.Run("Process all", func(t *testing.T) {
   149  		// Dismounting iteratively all queues should yield all nodes/blocks only once
   150  		// and in the proper order (parents are always evaluated first)
   151  		blocksInOrder := make([]*entity.ExecutableBlock, 0)
   152  
   153  		executionHeads := make(chan *Queue, 10)
   154  		executionHeads <- queue
   155  
   156  		for len(executionHeads) > 0 {
   157  			currentHead := <-executionHeads
   158  			block, newQueues := currentHead.Dismount()
   159  			blocksInOrder = append(blocksInOrder, block.(*entity.ExecutableBlock))
   160  			for _, newQueue := range newQueues {
   161  				executionHeads <- newQueue
   162  			}
   163  		}
   164  
   165  		// Couldn't find ready assertion for subset in order, so lets
   166  		// map nodes by their index and check if order is as expected
   167  		indices := make(map[*entity.ExecutableBlock]int)
   168  
   169  		for i, block := range blocksInOrder {
   170  			indices[block] = i
   171  		}
   172  
   173  		// a -> c -> b -> g
   174  		assert.Less(t, indices[a], indices[c])
   175  		assert.Less(t, indices[c], indices[b])
   176  		assert.Less(t, indices[b], indices[g])
   177  
   178  		// a -> c -> d -> f
   179  		assert.Less(t, indices[a], indices[c])
   180  		assert.Less(t, indices[c], indices[d])
   181  		assert.Less(t, indices[d], indices[f])
   182  
   183  		// a -> c -> d -> e
   184  		assert.Less(t, indices[a], indices[c])
   185  		assert.Less(t, indices[c], indices[d])
   186  		assert.Less(t, indices[d], indices[e])
   187  	})
   188  
   189  	//t.Run("Attaching", func(t *testing.T) {
   190  	//	queue := NewQueue(a)
   191  	//
   192  	//	added, new := queue.TryAdd(c)
   193  	//	assert.True(t, added)
   194  	//	assert.True(t, new)
   195  	//	assert.Equal(t, 2, queue.Size())
   196  	//	assert.Equal(t, uint64(1), queue.Height())
   197  	//
   198  	//	queueB := NewQueue(b)
   199  	//	added, new = queueB.TryAdd(g)
   200  	//	assert.True(t, added)
   201  	//	assert.True(t, new)
   202  	//
   203  	//	assert.Equal(t, 2, queueB.Size())
   204  	//	assert.Equal(t, uint64(1), queueB.Height())
   205  	//
   206  	//	queueF := NewQueue(f)
   207  	//
   208  	//	err := queue.Attach(queueF) // node D is missing
   209  	//	assert.Error(t, err)
   210  	//
   211  	//	err = queue.Attach(queueB)
   212  	//	assert.NoError(t, err)
   213  	//	assert.Equal(t, 4, queue.Size())
   214  	//	assert.Equal(t, uint64(3), queue.Height())
   215  	//
   216  	//	added, new = queue.TryAdd(d)
   217  	//	assert.True(t, added)
   218  	//	assert.True(t, new)
   219  	//	assert.Equal(t, 5, queue.Size())
   220  	//	assert.Equal(t, uint64(3), queue.Height())
   221  	//
   222  	//	err = queue.Attach(queueF) // node D is now in the queue
   223  	//	assert.NoError(t, err)
   224  	//	assert.Equal(t, 6, queue.Size())
   225  	//	assert.Equal(t, uint64(3), queue.Height())
   226  	//})
   227  
   228  	// Creating queue:
   229  	//    f--d--c-a
   230  	// Addingan element should be an idempotent operation:
   231  	//   * adding c a second time
   232  	//   * Dequeueing single head:
   233  	//     we should only get one child queue f--d--c
   234  	t.Run("Adding_Idempotent", func(t *testing.T) {
   235  		queue := NewQueue(a)
   236  		add, new := queue.TryAdd(c)
   237  		assert.True(t, add)
   238  		assert.True(t, new)
   239  
   240  		add, new = queue.TryAdd(d)
   241  		assert.True(t, add)
   242  		assert.True(t, new)
   243  
   244  		add, new = queue.TryAdd(f)
   245  		assert.True(t, add)
   246  		assert.True(t, new)
   247  
   248  		assert.Equal(t, 4, queue.Size())
   249  		assert.Equal(t, uint64(3), queue.Height())
   250  
   251  		// adding c a second time
   252  		add, new = queue.TryAdd(c)
   253  		assert.True(t, add)
   254  		assert.False(t, new)
   255  
   256  		// Dequeueing a
   257  		head, childQueues := queue.Dismount()
   258  		assert.Equal(t, a, head)
   259  		assert.Equal(t, 1, len(childQueues), "There should only be a single child queue")
   260  		assert.Equal(t, c.ID(), childQueues[0].Head.Item.ID())
   261  	})
   262  
   263  	// Testing attaching overlapping queues:
   264  	// queue A:
   265  	//   g-b
   266  	//      \
   267  	//       c
   268  	// queue B:
   269  	//    d--c-a
   270  	// attach queueA to queueB: we expect an error as the queues have nodes in common
   271  	//t.Run("Attaching_partially_overlapped_queue", func(t *testing.T) {
   272  	//	queueA := NewQueue(c)
   273  	//	add, new := queueA.TryAdd(b)
   274  	//	assert.True(t, add)
   275  	//	assert.True(t, new)
   276  	//
   277  	//	add, new = queueA.TryAdd(g)
   278  	//	assert.True(t, add)
   279  	//	assert.True(t, new)
   280  	//
   281  	//	queueB := NewQueue(a)
   282  	//	add, new = queueB.TryAdd(c)
   283  	//	assert.True(t, add)
   284  	//	assert.True(t, new)
   285  	//
   286  	//	add, new = queueB.TryAdd(d)
   287  	//	assert.True(t, add)
   288  	//	assert.True(t, new)
   289  	//
   290  	//	err := queueB.Attach(queueA)
   291  	//	assert.Error(t, err)
   292  	//})
   293  
   294  	t.Run("String()", func(t *testing.T) {
   295  		// a <- c <- d <- f
   296  		queue := NewQueue(a)
   297  		stored, _ := queue.TryAdd(c)
   298  		require.True(t, stored)
   299  		stored, _ = queue.TryAdd(d)
   300  		require.True(t, stored)
   301  		stored, _ = queue.TryAdd(f)
   302  		require.True(t, stored)
   303  		var builder strings.Builder
   304  		builder.WriteString(fmt.Sprintf("Header: %v\n", a.ID()))
   305  		builder.WriteString(fmt.Sprintf("Highest: %v\n", f.ID()))
   306  		builder.WriteString("Size: 4, Height: 3\n")
   307  		builder.WriteString(fmt.Sprintf("Node(height: %v): %v (children: 1)\n", a.Height(), a.ID()))
   308  		builder.WriteString(fmt.Sprintf("|- Node(height: %v): %v (children: 1)\n", c.Height(), c.ID()))
   309  		builder.WriteString(fmt.Sprintf("   |- Node(height: %v): %v (children: 1)\n", d.Height(), d.ID()))
   310  		builder.WriteString(fmt.Sprintf("      |- Node(height: %v): %v (children: 0)\n", f.Height(), f.ID()))
   311  		require.Equal(t, builder.String(), queue.String())
   312  	})
   313  }