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 }