blob: f2b535a2c6389a6375bebcc4b8983b1803698d5c [file] [log] [blame]
Tom Bergan4be9b972016-08-01 18:07:35 -07001// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http2
6
7import (
8 "bytes"
9 "fmt"
10 "sort"
11 "testing"
12)
13
14func defaultPriorityWriteScheduler() *priorityWriteScheduler {
15 return NewPriorityWriteScheduler(nil).(*priorityWriteScheduler)
16}
17
18func checkPriorityWellFormed(ws *priorityWriteScheduler) error {
19 for id, n := range ws.nodes {
20 if id != n.id {
21 return fmt.Errorf("bad ws.nodes: ws.nodes[%d] = %d", id, n.id)
22 }
23 if n.parent == nil {
24 if n.next != nil || n.prev != nil {
25 return fmt.Errorf("bad node %d: nil parent but prev/next not nil", id)
26 }
27 continue
28 }
29 found := false
30 for k := n.parent.kids; k != nil; k = k.next {
31 if k.id == id {
32 found = true
33 break
34 }
35 }
36 if !found {
37 return fmt.Errorf("bad node %d: not found in parent %d kids list", id, n.parent.id)
38 }
39 }
40 return nil
41}
42
43func fmtTree(ws *priorityWriteScheduler, fmtNode func(*priorityNode) string) string {
44 var ids []int
45 for _, n := range ws.nodes {
46 ids = append(ids, int(n.id))
47 }
48 sort.Ints(ids)
49
50 var buf bytes.Buffer
51 for _, id := range ids {
52 if buf.Len() != 0 {
53 buf.WriteString(" ")
54 }
55 if id == 0 {
56 buf.WriteString(fmtNode(&ws.root))
57 } else {
58 buf.WriteString(fmtNode(ws.nodes[uint32(id)]))
59 }
60 }
61 return buf.String()
62}
63
64func fmtNodeParentSkipRoot(n *priorityNode) string {
65 switch {
66 case n.id == 0:
67 return ""
68 case n.parent == nil:
69 return fmt.Sprintf("%d{parent:nil}", n.id)
70 default:
71 return fmt.Sprintf("%d{parent:%d}", n.id, n.parent.id)
72 }
73}
74
75func fmtNodeWeightParentSkipRoot(n *priorityNode) string {
76 switch {
77 case n.id == 0:
78 return ""
79 case n.parent == nil:
80 return fmt.Sprintf("%d{weight:%d,parent:nil}", n.id, n.weight)
81 default:
82 return fmt.Sprintf("%d{weight:%d,parent:%d}", n.id, n.weight, n.parent.id)
83 }
84}
85
86func TestPriorityTwoStreams(t *testing.T) {
87 ws := defaultPriorityWriteScheduler()
88 ws.OpenStream(1, OpenStreamOptions{})
89 ws.OpenStream(2, OpenStreamOptions{})
90
91 want := "1{weight:15,parent:0} 2{weight:15,parent:0}"
92 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
93 t.Errorf("After open\ngot %q\nwant %q", got, want)
94 }
95
96 // Move 1's parent to 2.
97 ws.AdjustStream(1, PriorityParam{
98 StreamDep: 2,
99 Weight: 32,
100 Exclusive: false,
101 })
102 want = "1{weight:32,parent:2} 2{weight:15,parent:0}"
103 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
104 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
105 }
106
107 if err := checkPriorityWellFormed(ws); err != nil {
108 t.Error(err)
109 }
110}
111
112func TestPriorityAdjustExclusiveZero(t *testing.T) {
113 // 1, 2, and 3 are all children of the 0 stream.
114 // Exclusive reprioritization to any of the streams should bring
115 // the rest of the streams under the reprioritized stream.
116 ws := defaultPriorityWriteScheduler()
117 ws.OpenStream(1, OpenStreamOptions{})
118 ws.OpenStream(2, OpenStreamOptions{})
119 ws.OpenStream(3, OpenStreamOptions{})
120
121 want := "1{weight:15,parent:0} 2{weight:15,parent:0} 3{weight:15,parent:0}"
122 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
123 t.Errorf("After open\ngot %q\nwant %q", got, want)
124 }
125
126 ws.AdjustStream(2, PriorityParam{
127 StreamDep: 0,
128 Weight: 20,
129 Exclusive: true,
130 })
131 want = "1{weight:15,parent:2} 2{weight:20,parent:0} 3{weight:15,parent:2}"
132 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
133 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
134 }
135
136 if err := checkPriorityWellFormed(ws); err != nil {
137 t.Error(err)
138 }
139}
140
141func TestPriorityAdjustOwnParent(t *testing.T) {
142 // Assigning a node as its own parent should have no effect.
143 ws := defaultPriorityWriteScheduler()
144 ws.OpenStream(1, OpenStreamOptions{})
145 ws.OpenStream(2, OpenStreamOptions{})
146 ws.AdjustStream(2, PriorityParam{
147 StreamDep: 2,
148 Weight: 20,
149 Exclusive: true,
150 })
151 want := "1{weight:15,parent:0} 2{weight:15,parent:0}"
152 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
153 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
154 }
155 if err := checkPriorityWellFormed(ws); err != nil {
156 t.Error(err)
157 }
158}
159
160func TestPriorityClosedStreams(t *testing.T) {
161 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{MaxClosedNodesInTree: 2}).(*priorityWriteScheduler)
162 ws.OpenStream(1, OpenStreamOptions{})
163 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
164 ws.OpenStream(3, OpenStreamOptions{PusherID: 2})
165 ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
166
167 // Close the first three streams. We lose 1, but keep 2 and 3.
168 ws.CloseStream(1)
169 ws.CloseStream(2)
170 ws.CloseStream(3)
171
172 want := "2{weight:15,parent:0} 3{weight:15,parent:2} 4{weight:15,parent:3}"
173 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
174 t.Errorf("After close\ngot %q\nwant %q", got, want)
175 }
176 if err := checkPriorityWellFormed(ws); err != nil {
177 t.Error(err)
178 }
179
180 // Adding a stream as an exclusive child of 1 gives it default
181 // priorities, since 1 is gone.
182 ws.OpenStream(5, OpenStreamOptions{})
183 ws.AdjustStream(5, PriorityParam{StreamDep: 1, Weight: 15, Exclusive: true})
184
185 // Adding a stream as an exclusive child of 2 should work, since 2 is not gone.
186 ws.OpenStream(6, OpenStreamOptions{})
187 ws.AdjustStream(6, PriorityParam{StreamDep: 2, Weight: 15, Exclusive: true})
188
189 want = "2{weight:15,parent:0} 3{weight:15,parent:6} 4{weight:15,parent:3} 5{weight:15,parent:0} 6{weight:15,parent:2}"
190 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
191 t.Errorf("After add streams\ngot %q\nwant %q", got, want)
192 }
193 if err := checkPriorityWellFormed(ws); err != nil {
194 t.Error(err)
195 }
196}
197
198func TestPriorityClosedStreamsDisabled(t *testing.T) {
199 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{}).(*priorityWriteScheduler)
200 ws.OpenStream(1, OpenStreamOptions{})
201 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
202 ws.OpenStream(3, OpenStreamOptions{PusherID: 2})
203
204 // Close the first two streams. We keep only 3.
205 ws.CloseStream(1)
206 ws.CloseStream(2)
207
208 want := "3{weight:15,parent:0}"
209 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
210 t.Errorf("After close\ngot %q\nwant %q", got, want)
211 }
212 if err := checkPriorityWellFormed(ws); err != nil {
213 t.Error(err)
214 }
215}
216
217func TestPriorityIdleStreams(t *testing.T) {
218 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{MaxIdleNodesInTree: 2}).(*priorityWriteScheduler)
219 ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 15}) // idle
220 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15}) // idle
221 ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20}) // idle
222 ws.OpenStream(4, OpenStreamOptions{})
223 ws.OpenStream(5, OpenStreamOptions{})
224 ws.OpenStream(6, OpenStreamOptions{})
225 ws.AdjustStream(4, PriorityParam{StreamDep: 1, Weight: 15})
226 ws.AdjustStream(5, PriorityParam{StreamDep: 2, Weight: 15})
227 ws.AdjustStream(6, PriorityParam{StreamDep: 3, Weight: 15})
228
229 want := "2{weight:15,parent:0} 3{weight:20,parent:2} 4{weight:15,parent:0} 5{weight:15,parent:2} 6{weight:15,parent:3}"
230 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
231 t.Errorf("After open\ngot %q\nwant %q", got, want)
232 }
233 if err := checkPriorityWellFormed(ws); err != nil {
234 t.Error(err)
235 }
236}
237
238func TestPriorityIdleStreamsDisabled(t *testing.T) {
239 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{}).(*priorityWriteScheduler)
240 ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 15}) // idle
241 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15}) // idle
242 ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20}) // idle
243 ws.OpenStream(4, OpenStreamOptions{})
244
245 want := "4{weight:15,parent:0}"
246 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
247 t.Errorf("After open\ngot %q\nwant %q", got, want)
248 }
249 if err := checkPriorityWellFormed(ws); err != nil {
250 t.Error(err)
251 }
252}
253
254func TestPrioritySection531NonExclusive(t *testing.T) {
255 // Example from RFC 7540 Section 5.3.1.
256 // A,B,C,D = 1,2,3,4
257 ws := defaultPriorityWriteScheduler()
258 ws.OpenStream(1, OpenStreamOptions{})
259 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
260 ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
261 ws.OpenStream(4, OpenStreamOptions{})
262 ws.AdjustStream(4, PriorityParam{
263 StreamDep: 1,
264 Weight: 15,
265 Exclusive: false,
266 })
267 want := "1{parent:0} 2{parent:1} 3{parent:1} 4{parent:1}"
268 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
269 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
270 }
271 if err := checkPriorityWellFormed(ws); err != nil {
272 t.Error(err)
273 }
274}
275
276func TestPrioritySection531Exclusive(t *testing.T) {
277 // Example from RFC 7540 Section 5.3.1.
278 // A,B,C,D = 1,2,3,4
279 ws := defaultPriorityWriteScheduler()
280 ws.OpenStream(1, OpenStreamOptions{})
281 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
282 ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
283 ws.OpenStream(4, OpenStreamOptions{})
284 ws.AdjustStream(4, PriorityParam{
285 StreamDep: 1,
286 Weight: 15,
287 Exclusive: true,
288 })
289 want := "1{parent:0} 2{parent:4} 3{parent:4} 4{parent:1}"
290 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
291 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
292 }
293 if err := checkPriorityWellFormed(ws); err != nil {
294 t.Error(err)
295 }
296}
297
298func makeSection533Tree() *priorityWriteScheduler {
299 // Initial tree from RFC 7540 Section 5.3.3.
300 // A,B,C,D,E,F = 1,2,3,4,5,6
301 ws := defaultPriorityWriteScheduler()
302 ws.OpenStream(1, OpenStreamOptions{})
303 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
304 ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
305 ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
306 ws.OpenStream(5, OpenStreamOptions{PusherID: 3})
307 ws.OpenStream(6, OpenStreamOptions{PusherID: 4})
308 return ws
309}
310
311func TestPrioritySection533NonExclusive(t *testing.T) {
312 // Example from RFC 7540 Section 5.3.3.
313 // A,B,C,D,E,F = 1,2,3,4,5,6
314 ws := defaultPriorityWriteScheduler()
315 ws.OpenStream(1, OpenStreamOptions{})
316 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
317 ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
318 ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
319 ws.OpenStream(5, OpenStreamOptions{PusherID: 3})
320 ws.OpenStream(6, OpenStreamOptions{PusherID: 4})
321 ws.AdjustStream(1, PriorityParam{
322 StreamDep: 4,
323 Weight: 15,
324 Exclusive: false,
325 })
326 want := "1{parent:4} 2{parent:1} 3{parent:1} 4{parent:0} 5{parent:3} 6{parent:4}"
327 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
328 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
329 }
330 if err := checkPriorityWellFormed(ws); err != nil {
331 t.Error(err)
332 }
333}
334
335func TestPrioritySection533Exclusive(t *testing.T) {
336 // Example from RFC 7540 Section 5.3.3.
337 // A,B,C,D,E,F = 1,2,3,4,5,6
338 ws := defaultPriorityWriteScheduler()
339 ws.OpenStream(1, OpenStreamOptions{})
340 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
341 ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
342 ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
343 ws.OpenStream(5, OpenStreamOptions{PusherID: 3})
344 ws.OpenStream(6, OpenStreamOptions{PusherID: 4})
345 ws.AdjustStream(1, PriorityParam{
346 StreamDep: 4,
347 Weight: 15,
348 Exclusive: true,
349 })
350 want := "1{parent:4} 2{parent:1} 3{parent:1} 4{parent:0} 5{parent:3} 6{parent:1}"
351 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
352 t.Errorf("After adjust\ngot %q\nwant %q", got, want)
353 }
354 if err := checkPriorityWellFormed(ws); err != nil {
355 t.Error(err)
356 }
357}
358
359func checkPopAll(ws WriteScheduler, order []uint32) error {
360 for k, id := range order {
361 wr, ok := ws.Pop()
362 if !ok {
363 return fmt.Errorf("Pop[%d]: got ok=false, want %d (order=%v)", k, id, order)
364 }
365 if got := wr.StreamID(); got != id {
366 return fmt.Errorf("Pop[%d]: got %v, want %d (order=%v)", k, got, id, order)
367 }
368 }
369 wr, ok := ws.Pop()
370 if ok {
371 return fmt.Errorf("Pop[%d]: got %v, want ok=false (order=%v)", len(order), wr.StreamID(), order)
372 }
373 return nil
374}
375
376func TestPriorityPopFrom533Tree(t *testing.T) {
377 ws := makeSection533Tree()
378
379 ws.Push(makeWriteHeadersRequest(3 /*C*/))
380 ws.Push(makeWriteNonStreamRequest())
381 ws.Push(makeWriteHeadersRequest(5 /*E*/))
382 ws.Push(makeWriteHeadersRequest(1 /*A*/))
383 t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot))
384
385 if err := checkPopAll(ws, []uint32{0 /*NonStream*/, 1, 3, 5}); err != nil {
386 t.Error(err)
387 }
388}
389
390func TestPriorityPopFromLinearTree(t *testing.T) {
391 ws := defaultPriorityWriteScheduler()
392 ws.OpenStream(1, OpenStreamOptions{})
393 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
394 ws.OpenStream(3, OpenStreamOptions{PusherID: 2})
395 ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
396
397 ws.Push(makeWriteHeadersRequest(3))
398 ws.Push(makeWriteHeadersRequest(4))
399 ws.Push(makeWriteHeadersRequest(1))
400 ws.Push(makeWriteHeadersRequest(2))
401 ws.Push(makeWriteNonStreamRequest())
402 ws.Push(makeWriteNonStreamRequest())
403 t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot))
404
405 if err := checkPopAll(ws, []uint32{0, 0 /*NonStreams*/, 1, 2, 3, 4}); err != nil {
406 t.Error(err)
407 }
408}
409
410func TestPriorityFlowControl(t *testing.T) {
411 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ThrottleOutOfOrderWrites: false})
412 ws.OpenStream(1, OpenStreamOptions{})
413 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
414
415 sc := &serverConn{maxFrameSize: 16}
416 st1 := &stream{id: 1, sc: sc}
417 st2 := &stream{id: 2, sc: sc}
418
419 ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 16), false}, st1, nil})
420 ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 16), false}, st2, nil})
421 ws.AdjustStream(2, PriorityParam{StreamDep: 1})
422
423 // No flow-control bytes available.
424 if wr, ok := ws.Pop(); ok {
425 t.Fatalf("Pop(limited by flow control)=%v,true, want false", wr)
426 }
427
428 // Add enough flow-control bytes to write st2 in two Pop calls.
429 // Should write data from st2 even though it's lower priority than st1.
430 for i := 1; i <= 2; i++ {
431 st2.flow.add(8)
432 wr, ok := ws.Pop()
433 if !ok {
434 t.Fatalf("Pop(%d)=false, want true", i)
435 }
436 if got, want := wr.DataSize(), 8; got != want {
Mikio Harab7883d22017-01-07 18:56:23 +0900437 t.Fatalf("Pop(%d)=%d bytes, want %d bytes", i, got, want)
Tom Bergan4be9b972016-08-01 18:07:35 -0700438 }
439 }
440}
441
442func TestPriorityThrottleOutOfOrderWrites(t *testing.T) {
443 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ThrottleOutOfOrderWrites: true})
444 ws.OpenStream(1, OpenStreamOptions{})
445 ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
446
447 sc := &serverConn{maxFrameSize: 4096}
448 st1 := &stream{id: 1, sc: sc}
449 st2 := &stream{id: 2, sc: sc}
450 st1.flow.add(4096)
451 st2.flow.add(4096)
452 ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 4096), false}, st2, nil})
453 ws.AdjustStream(2, PriorityParam{StreamDep: 1})
454
455 // We have enough flow-control bytes to write st2 in a single Pop call.
456 // However, due to out-of-order write throttling, the first call should
457 // only write 1KB.
458 wr, ok := ws.Pop()
459 if !ok {
460 t.Fatalf("Pop(st2.first)=false, want true")
461 }
462 if got, want := wr.StreamID(), uint32(2); got != want {
463 t.Fatalf("Pop(st2.first)=stream %d, want stream %d", got, want)
464 }
465 if got, want := wr.DataSize(), 1024; got != want {
466 t.Fatalf("Pop(st2.first)=%d bytes, want %d bytes", got, want)
467 }
468
469 // Now add data on st1. This should take precedence.
470 ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 4096), false}, st1, nil})
471 wr, ok = ws.Pop()
472 if !ok {
473 t.Fatalf("Pop(st1)=false, want true")
474 }
475 if got, want := wr.StreamID(), uint32(1); got != want {
476 t.Fatalf("Pop(st1)=stream %d, want stream %d", got, want)
477 }
478 if got, want := wr.DataSize(), 4096; got != want {
479 t.Fatalf("Pop(st1)=%d bytes, want %d bytes", got, want)
480 }
481
482 // Should go back to writing 1KB from st2.
483 wr, ok = ws.Pop()
484 if !ok {
485 t.Fatalf("Pop(st2.last)=false, want true")
486 }
487 if got, want := wr.StreamID(), uint32(2); got != want {
488 t.Fatalf("Pop(st2.last)=stream %d, want stream %d", got, want)
489 }
490 if got, want := wr.DataSize(), 1024; got != want {
491 t.Fatalf("Pop(st2.last)=%d bytes, want %d bytes", got, want)
492 }
493}
494
495func TestPriorityWeights(t *testing.T) {
496 ws := defaultPriorityWriteScheduler()
497 ws.OpenStream(1, OpenStreamOptions{})
498 ws.OpenStream(2, OpenStreamOptions{})
499
500 sc := &serverConn{maxFrameSize: 8}
501 st1 := &stream{id: 1, sc: sc}
502 st2 := &stream{id: 2, sc: sc}
503 st1.flow.add(40)
504 st2.flow.add(40)
505
506 ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 40), false}, st1, nil})
507 ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 40), false}, st2, nil})
508 ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 34})
509 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 9})
510
511 // st1 gets 3.5x the bandwidth of st2 (3.5 = (34+1)/(9+1)).
512 // The maximum frame size is 8 bytes. The write sequence should be:
513 // st1, total bytes so far is (st1=8, st=0)
514 // st2, total bytes so far is (st1=8, st=8)
515 // st1, total bytes so far is (st1=16, st=8)
516 // st1, total bytes so far is (st1=24, st=8) // 3x bandwidth
517 // st1, total bytes so far is (st1=32, st=8) // 4x bandwidth
518 // st2, total bytes so far is (st1=32, st=16) // 2x bandwidth
519 // st1, total bytes so far is (st1=40, st=16)
520 // st2, total bytes so far is (st1=40, st=24)
521 // st2, total bytes so far is (st1=40, st=32)
522 // st2, total bytes so far is (st1=40, st=40)
523 if err := checkPopAll(ws, []uint32{1, 2, 1, 1, 1, 2, 1, 2, 2, 2}); err != nil {
524 t.Error(err)
525 }
526}
Tom Bergane57319c2016-11-15 10:38:51 -0800527
528func TestPriorityRstStreamOnNonOpenStreams(t *testing.T) {
529 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{
530 MaxClosedNodesInTree: 0,
531 MaxIdleNodesInTree: 0,
532 })
533 ws.OpenStream(1, OpenStreamOptions{})
534 ws.CloseStream(1)
535 ws.Push(FrameWriteRequest{write: streamError(1, ErrCodeProtocol)})
536 ws.Push(FrameWriteRequest{write: streamError(2, ErrCodeProtocol)})
537
538 if err := checkPopAll(ws, []uint32{1, 2}); err != nil {
539 t.Error(err)
540 }
541}