Repository: quic-go/quic-go
Pull request #4851 (2025-01-08 06:43:35)

remove validation enforcing one Transport per net.PacketConn
It is invalid to use a `net.PacketConn` in multiple `Transport`s. However, the validation logic is causing pain when using wrapped `net.PacketConn`s. It was introduce to guard against incorrect uses of the API when the `Transport` was introduced, but this is probably less relevant now than it was back then.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/quic-go/quic-go/pull/4851?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 83.49%. Comparing base [(`c42f845`)](https://app.codecov.io/gh/quic-go/quic-go/commit/c42f8456ab2f493d9ea6af982115e204946d2045?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go) to head [(`05c7ca2`)](https://app.codecov.io/gh/quic-go/quic-go/commit/05c7ca26ca4f6c1fa544b71762a3c6a5b895bec0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #4851      +/-   ##
==========================================
- Coverage   83.73%   83.49%   -0.23%     
==========================================
  Files         149      145       -4     
  Lines       16095    15715     -380     
==========================================
- Hits        13476    13121     -355     
+ Misses       2089     2084       -5     
+ Partials      530      510      -20     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/quic-go/quic-go/pull/4851?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=quic-go).


Patch:
diff --git a/http3/transport.go b/http3/transport.go
index 8dcaef4d..92473969 100644
--- a/http3/transport.go
+++ b/http3/transport.go
@@ -216,20 +216,24 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
 	defer cl.useCount.Add(-1)
 	rsp, err := cl.rt.RoundTrip(req)
 	if err != nil {
-		// non-nil errors on roundtrip are likely due to a problem with the connection
-		// so we remove the client from the cache so that subsequent trips reconnect
-		// context cancelation is excluded as is does not signify a connection error
-		if !errors.Is(err, context.Canceled) {
-			t.removeClient(hostname)
+		select {
+		case <-req.Context().Done():
+			return nil, err
+		default:
 		}
 
+		// Non-nil errors are likely might be due to a problem with the connection,
+		// so we remove the client from the cache so that subsequent calls reconnect.
+		t.removeClient(hostname)
 		if isReused {
 			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
 				return t.RoundTripOpt(req, opt)
 			}
 		}
+
+		return nil, err
 	}
-	return rsp, err
+	return rsp, nil
 }
 
 // RoundTrip does a round trip.
diff --git a/multiplexer.go b/multiplexer.go
deleted file mode 100644
index cebe7c2e..00000000
--- a/multiplexer.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package quic
-
-import (
-	"fmt"
-	"net"
-	"sync"
-
-	"github.com/quic-go/quic-go/internal/utils"
-)
-
-var (
-	connMuxerOnce sync.Once
-	connMuxer     multiplexer
-)
-
-type indexableConn interface{ LocalAddr() net.Addr }
-
-type multiplexer interface {
-	AddConn(conn indexableConn)
-	RemoveConn(indexableConn) error
-}
-
-// The connMultiplexer listens on multiple net.PacketConns and dispatches
-// incoming packets to the connection handler.
-type connMultiplexer struct {
-	mutex sync.Mutex
-
-	conns  map[string] /* LocalAddr().String() */ indexableConn
-	logger utils.Logger
-}
-
-var _ multiplexer = &connMultiplexer{}
-
-func getMultiplexer() multiplexer {
-	connMuxerOnce.Do(func() {
-		connMuxer = &connMultiplexer{
-			conns:  make(map[string]indexableConn),
-			logger: utils.DefaultLogger.WithPrefix("muxer"),
-		}
-	})
-	return connMuxer
-}
-
-func (m *connMultiplexer) index(addr net.Addr) string {
-	return addr.Network() + " " + addr.String()
-}
-
-func (m *connMultiplexer) AddConn(c indexableConn) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-
-	connIndex := m.index(c.LocalAddr())
-	p, ok := m.conns[connIndex]
-	if ok {
-		// Panics if we're already listening on this connection.
-		// This is a safeguard because we're introducing a breaking API change, see
-		// https://github.com/quic-go/quic-go/issues/3727 for details.
-		// We'll remove this at a later time, when most users of the library have made the switch.
-		panic("connection already exists") // TODO: write a nice message
-	}
-	m.conns[connIndex] = p
-}
-
-func (m *connMultiplexer) RemoveConn(c indexableConn) error {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-
-	connIndex := m.index(c.LocalAddr())
-	if _, ok := m.conns[connIndex]; !ok {
-		return fmt.Errorf("cannote remove connection %s, connection is unknown", connIndex)
-	}
-
-	delete(m.conns, connIndex)
-	return nil
-}
diff --git a/server.go b/server.go
index c9395edf..cece797b 100644
--- a/server.go
+++ b/server.go
@@ -105,10 +105,18 @@ type baseServer struct {
 		protocol.Version,
 	) quicConn
 
-	closeMx   sync.Mutex
-	errorChan chan struct{} // is closed when the server is closed
-	closeErr  error
-	running   chan struct{} // closed as soon as run() returns
+	closeMx sync.Mutex
+	// errorChan is closed when Close is called. This has two effects:
+	// 1. it cancels handshakes that are still in flight (using CONNECTION_REFUSED) errors
+	// 2. it stops handling of packets passed to this server
+	errorChan chan struct{}
+	// acceptChan is closed when Close returns.
+	// This only happens once all handshake in flight have either completed and canceled.
+	// Calls to Accept will first drain the queue of connections that have completed the handshake,
+	// and then return ErrServerClosed.
+	stopAccepting chan struct{}
+	closeErr      error
+	running       chan struct{} // closed as soon as run() returns
 
 	versionNegotiationQueue chan receivedPacket
 	invalidTokenQueue       chan rejectedPacket
@@ -263,6 +271,7 @@ func newServer(
 		connHandler:               connHandler,
 		connQueue:                 make(chan quicConn, protocol.MaxAcceptQueueSize),
 		errorChan:                 make(chan struct{}),
+		stopAccepting:             make(chan struct{}),
 		running:                   make(chan struct{}),
 		receivedPackets:           make(chan receivedPacket, protocol.MaxServerUnprocessedPackets),
 		versionNegotiationQueue:   make(chan receivedPacket, 4),
@@ -333,15 +342,19 @@ func (s *baseServer) accept(ctx context.Context) (quicConn, error) {
 		return nil, ctx.Err()
 	case conn := <-s.connQueue:
 		return conn, nil
-	case <-s.errorChan:
+	case <-s.stopAccepting:
+		// first drain the queue
+		select {
+		case conn := <-s.connQueue:
+			return conn, nil
+		default:
+		}
 		return nil, s.closeErr
 	}
 }
 
 func (s *baseServer) Close() error {
 	s.close(ErrServerClosed, true)
-	// wait until all handshakes in flight have terminated
-	s.handshakingCount.Wait()
 	return nil
 }
 
@@ -359,6 +372,9 @@ func (s *baseServer) close(e error, notifyOnClose bool) {
 	if notifyOnClose {
 		s.onClose()
 	}
+	// wait until all handshakes in flight have terminated
+	s.handshakingCount.Wait()
+	close(s.stopAccepting)
 }
 
 // Addr returns the server's network address
@@ -369,6 +385,8 @@ func (s *baseServer) Addr() net.Addr {
 func (s *baseServer) handlePacket(p receivedPacket) {
 	select {
 	case s.receivedPackets <- p:
+	case <-s.errorChan:
+		return
 	default:
 		s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size())
 		if s.tracer != nil && s.tracer.DroppedPacket != nil {
@@ -719,42 +737,39 @@ func (s *baseServer) handleInitialImpl(p receivedPacket, hdr *wire.Header) error
 	s.handshakingCount.Add(1)
 	go func() {
 		defer s.handshakingCount.Done()
-		if completed := s.handleNewConn(conn); !completed {
-			return
-		}
-
-		select {
-		case s.connQueue <- conn:
-		default:
-			conn.closeWithTransportError(ConnectionRefused)
-		}
+		s.handleNewConn(conn)
 	}()
 	go conn.run()
 	return nil
 }
 
-func (s *baseServer) handleNewConn(conn quicConn) bool {
+func (s *baseServer) handleNewConn(conn quicConn) {
 	if s.acceptEarlyConns {
 		// wait until the early connection is ready, the handshake fails, or the server is closed
 		select {
 		case <-s.errorChan:
 			conn.closeWithTransportError(ConnectionRefused)
-			return false
+			return
 		case <-conn.Context().Done():
-			return false
+			return
 		case <-conn.earlyConnReady():
-			return true
+		}
+	} else {
+		// wait until the handshake completes, fails, or the server is closed
+		select {
+		case <-s.errorChan:
+			conn.closeWithTransportError(ConnectionRefused)
+			return
+		case <-conn.Context().Done():
+			return
+		case <-conn.HandshakeComplete():
 		}
 	}
-	// wait until the handshake completes, fails, or the server is closed
+
 	select {
-	case <-s.errorChan:
+	case s.connQueue <- conn:
+	default:
 		conn.closeWithTransportError(ConnectionRefused)
-		return false
-	case <-conn.Context().Done():
-		return false
-	case <-conn.HandshakeComplete():
-		return true
 	}
 }
 
diff --git a/transport.go b/transport.go
index d2e72ef8..dd69ded5 100644
--- a/transport.go
+++ b/transport.go
@@ -269,7 +269,6 @@ func (t *Transport) init(allowZeroLengthConnIDs bool) error {
 			t.connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: t.connIDLen}
 		}
 
-		getMultiplexer().AddConn(t.Conn)
 		go t.listen(conn)
 		go t.runSendQueue()
 	})
@@ -366,7 +365,6 @@ var setBufferWarningOnce sync.Once
 
 func (t *Transport) listen(conn rawConn) {
 	defer close(t.listening)
-	defer getMultiplexer().RemoveConn(t.Conn)
 
 	for {
 		p, err := conn.ReadPacket()


Test patch:
diff --git a/client_test.go b/client_test.go
index 98d6e2a8..2bfe3cb9 100644
--- a/client_test.go
+++ b/client_test.go
@@ -17,20 +17,14 @@ import (
 	"go.uber.org/mock/gomock"
 )
 
-type nullMultiplexer struct{}
-
-func (n nullMultiplexer) AddConn(indexableConn)          {}
-func (n nullMultiplexer) RemoveConn(indexableConn) error { return nil }
-
 var _ = Describe("Client", func() {
 	var (
-		cl              *client
-		packetConn      *MockSendConn
-		connID          protocol.ConnectionID
-		origMultiplexer multiplexer
-		tlsConf         *tls.Config
-		tracer          *mocklogging.MockConnectionTracer
-		config          *Config
+		cl         *client
+		packetConn *MockSendConn
+		connID     protocol.ConnectionID
+		tlsConf    *tls.Config
+		tracer     *mocklogging.MockConnectionTracer
+		config     *Config
 
 		originalClientConnConstructor func(
 			ctx context.Context,
@@ -74,14 +68,9 @@ var _ = Describe("Client", func() {
 			tracer:     tr,
 			logger:     utils.DefaultLogger,
 		}
-		getMultiplexer() // make the sync.Once execute
-		// replace the clientMuxer. getMultiplexer will now return the nullMultiplexer
-		origMultiplexer = connMuxer
-		connMuxer = &nullMultiplexer{}
 	})
 
 	AfterEach(func() {
-		connMuxer = origMultiplexer
 		newClientConnection = originalClientConnConstructor
 	})
 
diff --git a/frame_sorter_test.go b/frame_sorter_test.go
index 9a684c91..13015e93 100644
--- a/frame_sorter_test.go
+++ b/frame_sorter_test.go
@@ -1,1528 +1,1469 @@
 package quic
 
 import (
-	"bytes"
 	"fmt"
 	"math"
-	"time"
+	"testing"
 
 	"golang.org/x/exp/rand"
 
 	"github.com/quic-go/quic-go/internal/protocol"
 
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/require"
 )
 
-var _ = Describe("frame sorter", func() {
-	var s *frameSorter
+type callbackTracker struct {
+	called *bool
+	cb     func()
+}
 
-	checkGaps := func(expectedGaps []byteInterval) {
+func (t *callbackTracker) WasCalled() bool { return *t.called }
+
+func getFrameSorterTestCallback(t *testing.T) (func(), callbackTracker) {
+	var called bool
+	cb := func() {
+		if called {
+			t.Fatal("double free")
+		}
+		called = true
+	}
+	return cb, callbackTracker{
+		cb:     cb,
+		called: &called,
+	}
+}
+
+func TestFrameSorterSimpleCases(t *testing.T) {
+	s := newFrameSorter()
+	_, data, doneCb := s.Pop()
+	require.Nil(t, data)
+	require.Nil(t, doneCb)
+
+	// empty frames are ignored
+	require.NoError(t, s.Push(nil, 0, nil))
+	_, data, doneCb = s.Pop()
+	require.Nil(t, data)
+	require.Nil(t, doneCb)
+
+	cb1, t1 := getFrameSorterTestCallback(t)
+	cb2, t2 := getFrameSorterTestCallback(t)
+	require.NoError(t, s.Push([]byte("bar"), 3, cb2))
+	require.True(t, s.HasMoreData())
+	require.NoError(t, s.Push([]byte("foo"), 0, cb1))
+
+	offset, data, doneCb := s.Pop()
+	require.Equal(t, []byte("foo"), data)
+	require.Zero(t, offset)
+	require.NotNil(t, doneCb)
+	doneCb()
+	require.True(t, t1.WasCalled())
+	require.False(t, t2.WasCalled())
+	require.True(t, s.HasMoreData())
+
+	offset, data, doneCb = s.Pop()
+	require.Equal(t, []byte("bar"), data)
+	require.Equal(t, protocol.ByteCount(3), offset)
+	require.NotNil(t, doneCb)
+	doneCb()
+	require.True(t, t2.WasCalled())
+	require.False(t, s.HasMoreData())
+
+	// now receive a duplicate
+	cb3, t3 := getFrameSorterTestCallback(t)
+	require.NoError(t, s.Push([]byte("foo"), 0, cb3))
+	require.False(t, s.HasMoreData())
+	require.True(t, t3.WasCalled())
+
+	// now receive a later frame that overlaps with the ones we already consumed
+	cb4, _ := getFrameSorterTestCallback(t)
+	require.NoError(t, s.Push([]byte("barbaz"), 3, cb4))
+	require.True(t, s.HasMoreData())
+
+	offset, data, _ = s.Pop()
+	require.Equal(t, protocol.ByteCount(6), offset)
+	require.Equal(t, []byte("baz"), data)
+	require.False(t, s.HasMoreData())
+}
+
+// Usually, it's not a good idea to test the implementation details.
+// However, we need to make sure that the frame sorter handles gaps correctly,
+// in particular when overlapping stream data is received.
+// This also includes returning buffers that are no longer needed.
+func TestFrameSorterGapHandling(t *testing.T) {
+	getData := func(l protocol.ByteCount) []byte {
+		b := make([]byte, l)
+		rand.Read(b)
+		return b
+	}
+
+	checkQueue := func(t *testing.T, s *frameSorter, m map[protocol.ByteCount][]byte) {
+		require.Equal(t, len(m), len(s.queue))
+		for offset, data := range m {
+			require.Contains(t, s.queue, offset)
+			require.Equal(t, data, s.queue[offset].Data)
+		}
+	}
+
+	checkGaps := func(t *testing.T, s *frameSorter, expectedGaps []byteInterval) {
 		if s.gaps.Len() != len(expectedGaps) {
 			fmt.Println("Gaps:")
 			for gap := s.gaps.Front(); gap != nil; gap = gap.Next() {
 				fmt.Printf("\t%d - %d\n", gap.Value.Start, gap.Value.End)
 			}
-			ExpectWithOffset(1, s.gaps.Len()).To(Equal(len(expectedGaps)))
+			require.Equal(t, len(expectedGaps), s.gaps.Len())
 		}
 		var i int
 		for gap := s.gaps.Front(); gap != nil; gap = gap.Next() {
-			ExpectWithOffset(1, gap.Value).To(Equal(expectedGaps[i]))
+			require.Equal(t, expectedGaps[i], gap.Value)
 			i++
 		}
 	}
 
-	type callbackTracker struct {
-		called *bool
-		cb     func()
-	}
+	// ---xxx--------------
+	//       ++++++
+	// =>
+	// ---xxx++++++--------
+	t.Run("case 1", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 11, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+	})
 
-	getCallback := func() (func(), callbackTracker) {
-		var called bool
-		cb := func() {
-			if called {
-				panic("double free")
-			}
-			called = true
-		}
-		return cb, callbackTracker{
-			cb:     cb,
-			called: &called,
-		}
-	}
+	// ---xxx-----------------
+	//          +++++++
+	// =>
+	// ---xxx---+++++++--------
+	t.Run("case 2", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f2, 10, cb2)) // 10 -15
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			10: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 6, End: 10},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+	})
 
-	checkCallbackCalled := func(t callbackTracker) {
-		ExpectWithOffset(1, *t.called).To(BeTrue())
-	}
+	// ---xxx----xxxxxx-------
+	//       ++++
+	// =>
+	// ---xxx++++xxxxx--------
+	t.Run("case 3", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f3, 10, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f2, 6, cb3))  // 6 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			6:  f2,
+			10: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-	checkCallbackNotCalled := func(t callbackTracker) {
-		ExpectWithOffset(1, *t.called).To(BeFalse())
-		t.cb()
-		ExpectWithOffset(1, *t.called).To(BeTrue())
-	}
+	// ----xxxx-------
+	//       ++++
+	// =>
+	// ----xxxx++-----
+	t.Run("case 4", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 7
+		require.NoError(t, s.Push(f2, 5, cb2)) // 5 - 9
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			7: f2[2:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 9, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+	})
 
-	BeforeEach(func() {
-		s = newFrameSorter()
+	t.Run("case 4, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
+		f1 := getData(4 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 7
+		require.NoError(t, s.Push(f2, 5*mult, cb2)) // 5 - 9
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f1,
+			7 * mult: f2[2*mult:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 9 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
 	})
 
-	It("returns nil when empty", func() {
-		_, data, doneCb := s.Pop()
-		Expect(data).To(BeNil())
-		Expect(doneCb).To(BeNil())
+	// xxxx-------
+	//    ++++
+	// =>
+	// xxxx+++-----
+	t.Run("case 5", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 0, cb1)) // 0 - 4
+		require.NoError(t, s.Push(f2, 3, cb2)) // 3 - 7
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			0: f1,
+			4: f2[1:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 7, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
 	})
 
-	It("inserts and pops a single frame", func() {
-		cb, t := getCallback()
-		Expect(s.Push([]byte("foobar"), 0, cb)).To(Succeed())
-		offset, data, doneCb := s.Pop()
-		Expect(offset).To(BeZero())
-		Expect(data).To(Equal([]byte("foobar")))
-		Expect(doneCb).ToNot(BeNil())
-		checkCallbackNotCalled(t)
-		offset, data, doneCb = s.Pop()
-		Expect(offset).To(Equal(protocol.ByteCount(6)))
-		Expect(data).To(BeNil())
-		Expect(doneCb).To(BeNil())
+	t.Run("case 5, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
+		f1 := getData(4 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 0, cb1))      // 0 - 4
+		require.NoError(t, s.Push(f2, 3*mult, cb2)) // 3 - 7
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			0:        f1,
+			4 * mult: f2[mult:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 7 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
 	})
 
-	It("inserts and pops two consecutive frame", func() {
-		cb1, t1 := getCallback()
-		cb2, t2 := getCallback()
-		Expect(s.Push([]byte("bar"), 3, cb2)).To(Succeed())
-		Expect(s.Push([]byte("foo"), 0, cb1)).To(Succeed())
-		offset, data, doneCb := s.Pop()
-		Expect(offset).To(BeZero())
-		Expect(data).To(Equal([]byte("foo")))
-		Expect(doneCb).ToNot(BeNil())
-		doneCb()
-		checkCallbackCalled(t1)
-		offset, data, doneCb = s.Pop()
-		Expect(offset).To(Equal(protocol.ByteCount(3)))
-		Expect(data).To(Equal([]byte("bar")))
-		Expect(doneCb).ToNot(BeNil())
-		doneCb()
-		checkCallbackCalled(t2)
-		offset, data, doneCb = s.Pop()
-		Expect(offset).To(Equal(protocol.ByteCount(6)))
-		Expect(data).To(BeNil())
-		Expect(doneCb).To(BeNil())
+	// ----xxxx-------
+	//   ++++
+	// =>
+	// --++xxxx-------
+	t.Run("case 6", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 9
+		require.NoError(t, s.Push(f2, 3, cb2)) // 3 - 7
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f2[:2],
+			5: f1,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 9, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
 	})
 
-	It("ignores empty frames", func() {
-		Expect(s.Push(nil, 0, nil)).To(Succeed())
-		_, data, doneCb := s.Pop()
-		Expect(data).To(BeNil())
-		Expect(doneCb).To(BeNil())
+	t.Run("case 6, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
+		f1 := getData(4 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5*mult, cb1)) // 5 - 9
+		require.NoError(t, s.Push(f2, 3*mult, cb2)) // 3 - 7
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f2[:2*mult],
+			5 * mult: f1,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 9 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
 	})
 
-	It("says if has more data", func() {
-		Expect(s.HasMoreData()).To(BeFalse())
-		Expect(s.Push([]byte("foo"), 0, nil)).To(Succeed())
-		Expect(s.HasMoreData()).To(BeTrue())
-		_, data, _ := s.Pop()
-		Expect(data).To(Equal([]byte("foo")))
-		Expect(s.HasMoreData()).To(BeFalse())
+	// ---xxx----xxxxxx-------
+	//       ++
+	// =>
+	// ---xxx++--xxxxx--------
+	t.Run("case 7", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(2)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f3, 10, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f2, 6, cb3))  // 6 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			6:  f2,
+			10: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 8, End: 10},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
 	})
 
-	Context("Gap handling", func() {
-		var dataCounter uint8
+	// ---xxx---------xxxxxx--
+	//          ++
+	// =>
+	// ---xxx---++----xxxxx--
+	t.Run("case 8", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(2)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f3, 15, cb2)) // 15 - 20
+		require.NoError(t, s.Push(f2, 10, cb3)) // 10 - 12
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			10: f2,
+			15: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 6, End: 10},
+			{Start: 12, End: 15},
+			{Start: 20, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-		BeforeEach(func() {
-			dataCounter = 0
+	// ---xxx----xxxxxx-------
+	//         ++
+	// =>
+	// ---xxx--++xxxxx--------
+	t.Run("case 9", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(2)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f3, 10, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f2, 8, cb3))  // 8 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			8:  f2,
+			10: f3,
 		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 6, End: 8},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-		checkQueue := func(m map[protocol.ByteCount][]byte) {
-			ExpectWithOffset(1, s.queue).To(HaveLen(len(m)))
-			for offset, data := range m {
-				ExpectWithOffset(1, s.queue).To(HaveKey(offset))
-				ExpectWithOffset(1, s.queue[offset].Data).To(Equal(data))
-			}
-		}
+	// ---xxx----=====-------
+	//      +++++++
+	// =>
+	// ---xxx++++=====--------
+	t.Run("case 10", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f2, 10, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f3, 5, cb3))  // 5 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			6:  f3[1:5],
+			10: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
 
-		getData := func(l protocol.ByteCount) []byte {
-			dataCounter++
-			return bytes.Repeat([]byte{dataCounter}, int(l))
-		}
+	t.Run("case 10, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 4))
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f2, 10*mult, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f3, 5*mult, cb3))  // 5 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult:  f1,
+			6 * mult:  f3[mult : 5*mult],
+			10 * mult: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 15 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-		// ---xxx--------------
-		//       ++++++
-		// =>
-		// ---xxx++++++--------
-		It("case 1", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(5)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 11, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ---xxx-----------------
-		//          +++++++
-		// =>
-		// ---xxx---+++++++--------
-		It("case 2", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(5)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 -15
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				10: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 6, End: 10},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ---xxx----xxxxxx-------
-		//       ++++
-		// =>
-		// ---xxx++++xxxxx--------
-		It("case 3", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			f3 := getData(5)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f3, 10, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f2, 6, cb3)).To(Succeed())  // 6 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				6:  f2,
-				10: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ----xxxx-------
-		//       ++++
-		// =>
-		// ----xxxx++-----
-		It("case 4", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 7
-			Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 9
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				7: f2[2:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 9, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		It("case 4, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
-			f1 := getData(4 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 7
-			Expect(s.Push(f2, 5*mult, cb2)).To(Succeed()) // 5 - 9
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f1,
-				7 * mult: f2[2*mult:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 9 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// xxxx-------
-		//    ++++
-		// =>
-		// xxxx+++-----
-		It("case 5", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 0, cb1)).To(Succeed()) // 0 - 4
-			Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 7
-			checkQueue(map[protocol.ByteCount][]byte{
-				0: f1,
-				4: f2[1:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 7, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		It("case 5, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
-			f1 := getData(4 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 0, cb1)).To(Succeed())      // 0 - 4
-			Expect(s.Push(f2, 3*mult, cb2)).To(Succeed()) // 3 - 7
-			checkQueue(map[protocol.ByteCount][]byte{
-				0:        f1,
-				4 * mult: f2[mult:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 7 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ----xxxx-------
-		//   ++++
-		// =>
-		// --++xxxx-------
-		It("case 6", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 9
-			Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 7
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f2[:2],
-				5: f1,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 9, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		It("case 6, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
-			f1 := getData(4 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5*mult, cb1)).To(Succeed()) // 5 - 9
-			Expect(s.Push(f2, 3*mult, cb2)).To(Succeed()) // 3 - 7
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f2[:2*mult],
-				5 * mult: f1,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 9 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ---xxx----xxxxxx-------
-		//       ++
-		// =>
-		// ---xxx++--xxxxx--------
-		It("case 7", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(2)
-			cb2, t2 := getCallback()
-			f3 := getData(5)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f3, 10, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f2, 6, cb3)).To(Succeed())  // 6 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				6:  f2,
-				10: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 8, End: 10},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx---------xxxxxx--
-		//          ++
-		// =>
-		// ---xxx---++----xxxxx--
-		It("case 8", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(2)
-			cb2, t2 := getCallback()
-			f3 := getData(5)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f3, 15, cb2)).To(Succeed()) // 15 - 20
-			Expect(s.Push(f2, 10, cb3)).To(Succeed()) // 10 - 12
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				10: f2,
-				15: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 6, End: 10},
-				{Start: 12, End: 15},
-				{Start: 20, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx----xxxxxx-------
-		//         ++
-		// =>
-		// ---xxx--++xxxxx--------
-		It("case 9", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(2)
-			cb2, t2 := getCallback()
-			cb3, t3 := getCallback()
-			f3 := getData(5)
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f3, 10, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f2, 8, cb3)).To(Succeed())  // 8 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				8:  f2,
-				10: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 6, End: 8},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx----=====-------
-		//      +++++++
-		// =>
-		// ---xxx++++=====--------
-		It("case 10", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(5)
-			cb2, t2 := getCallback()
-			f3 := getData(6)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f3, 5, cb3)).To(Succeed())  // 5 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				6:  f3[1:5],
-				10: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 10, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 4))
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(5 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(6 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f2, 10*mult, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f3, 5*mult, cb3)).To(Succeed())  // 5 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult:  f1,
-				6 * mult:  f3[mult : 5*mult],
-				10 * mult: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 15 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxxx----=====-------
-		//      ++++++
-		// =>
-		// ---xxx++++=====--------
-		It("case 11", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(5)
-			cb2, t2 := getCallback()
-			f3 := getData(5)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 7
-			Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f3, 5, cb3)).To(Succeed())  // 5 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				7:  f3[2:],
-				10: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		// ---xxxx----=====-------
-		//      ++++++
-		// =>
-		// ---xxx++++=====--------
-		It("case 11, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 3))
-			f1 := getData(4 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(5 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(5 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed())  // 3 - 7
-			Expect(s.Push(f2, 10*mult, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f3, 5*mult, cb3)).To(Succeed())  // 5 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult:  f1,
-				7 * mult:  f3[2*mult:],
-				10 * mult: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 15 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ----xxxx-------
-		//     +++++++
-		// =>
-		// ----+++++++-----
-		It("case 12", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(7)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 7
-			Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ----xxx===-------
-		//     +++++++
-		// =>
-		// ----+++++++-----
-		It("case 13", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(7)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ----xxx====-------
-		//     +++++
-		// =>
-		// ----+++====-----
-		It("case 14", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			f3 := getData(5)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 10
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f3[:3],
-				6: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 14, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 3))
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(5 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 10
-			Expect(s.Push(f3, 3*mult, cb3)).To(Succeed()) // 3 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f3[:3*mult],
-				6 * mult: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 10 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ----xxx===-------
-		//     ++++++
-		// =>
-		// ----++++++-----
-		It("case 15", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(6)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 9
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 9, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxxx-------
-		//    ++++
-		// =>
-		// ---xxxx-----
-		It("case 16", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 9
-			Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 9
-			checkQueue(map[protocol.ByteCount][]byte{
-				5: f1,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 5},
-				{Start: 9, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		// ----xxx===-------
-		//     +++
-		// =>
-		// ----xxx===-----
-		It("case 17", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(3)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 6
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 9, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		// ---xxxx-------
-		//    ++
-		// =>
-		// ---xxxx-----
-		It("case 18", func() {
-			f1 := getData(4)
-			cb1, t1 := getCallback()
-			f2 := getData(2)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 9
-			Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 7
-			checkQueue(map[protocol.ByteCount][]byte{
-				5: f1,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 5},
-				{Start: 9, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		// ---xxxxx------
-		//     ++
-		// =>
-		// ---xxxxx----
-		It("case 19", func() {
-			f1 := getData(5)
-			cb1, t1 := getCallback()
-			f2 := getData(2)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 10
-			checkQueue(map[protocol.ByteCount][]byte{
-				5: f1,
-			})
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				5: f1,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 5},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		// xxxxx------
-		//  ++
-		// =>
-		// xxxxx------
-		It("case 20", func() {
-			f1 := getData(10)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 0, cb1)).To(Succeed()) // 0 - 10
-			Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 9
-			checkQueue(map[protocol.ByteCount][]byte{
-				0: f1,
-			})
-			checkGaps([]byteInterval{
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		// ---xxxxx---
-		//      +++
-		// =>
-		// ---xxxxx---
-		It("case 21", func() {
-			f1 := getData(5)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 10
-			Expect(s.Push(f2, 7, cb2)).To(Succeed()) // 7 - 10
-			checkGaps([]byteInterval{
-				{Start: 0, End: 5},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkQueue(map[protocol.ByteCount][]byte{
-				5: f1,
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-		})
-
-		// ----xxx------
-		//   +++++
-		// =>
-		// --+++++----
-		It("case 22", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(5)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 8
-			Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 8, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ----xxx===------
-		//   ++++++++
-		// =>
-		// --++++++++----
-		It("case 23", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(8)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 8
-			Expect(s.Push(f2, 8, cb2)).To(Succeed()) // 8 - 11
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 11, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// --xxx---===---
-		//      ++++++
-		// =>
-		// --xxx++++++----
-		It("case 24", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(6)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12
-			Expect(s.Push(f3, 6, cb3)).To(Succeed()) // 6 - 12
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 12, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// --xxx---===---###
-		//      +++++++++
-		// =>
-		// --xxx+++++++++###
-		It("case 25", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(3)
-			cb3, t3 := getCallback()
-			f4 := getData(9)
-			cb4, t4 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed())  // 3 - 6
-			Expect(s.Push(f2, 9, cb2)).To(Succeed())  // 9 - 12
-			Expect(s.Push(f3, 15, cb3)).To(Succeed()) // 15 - 18
-			Expect(s.Push(f4, 6, cb4)).To(Succeed())  // 6 - 15
-			checkQueue(map[protocol.ByteCount][]byte{
-				3:  f1,
-				6:  f4,
-				15: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 18, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-			checkCallbackNotCalled(t4)
-		})
-
-		// ----xxx------
-		//   +++++++
-		// =>
-		// --+++++++---
-		It("case 26", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(10)
-			cb2, t2 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 8
-			Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 13
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 13, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-		})
-
-		// ---xxx====---
-		//   ++++
-		// =>
-		// --+xxx====---
-		It("case 27", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			f3 := getData(4)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 10
-			Expect(s.Push(f3, 2, cb3)).To(Succeed()) // 2 - 6
-			checkQueue(map[protocol.ByteCount][]byte{
-				2: f3[:1],
-				3: f1,
-				6: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 2},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 27, for long frames", func() {
-			const mult = protocol.MinStreamFrameSize
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(4 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 10
-			Expect(s.Push(f3, 2*mult, cb3)).To(Succeed()) // 2 - 6
-			checkQueue(map[protocol.ByteCount][]byte{
-				2 * mult: f3[:mult],
-				3 * mult: f1,
-				6 * mult: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 2 * mult},
-				{Start: 10 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx====---
-		//   ++++++
-		// =>
-		// --+xxx====---
-		It("case 28", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			f3 := getData(6)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 10
-			Expect(s.Push(f3, 2, cb3)).To(Succeed()) // 2 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				2: f3[:1],
-				3: f1,
-				6: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 2},
-				{Start: 10, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 28, for long frames", func() {
-			const mult = protocol.MinStreamFrameSize
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(6 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 10
-			Expect(s.Push(f3, 2*mult, cb3)).To(Succeed()) // 2 - 8
-			checkQueue(map[protocol.ByteCount][]byte{
-				2 * mult: f3[:mult],
-				3 * mult: f1,
-				6 * mult: f2,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 2 * mult},
-				{Start: 10 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx===-----
-		//       +++++
-		// =>
-		// ---xxx+++++---
-		It("case 29", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(5)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9
-			Expect(s.Push(f3, 6, cb3)).To(Succeed()) // 6 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 11, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx===----
-		//      ++++++
-		// =>
-		// ---xxx===++--
-		It("case 30", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(6)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9
-			Expect(s.Push(f3, 5, cb3)).To(Succeed()) // 5 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f2,
-				9: f3[4:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 11, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 30, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(3 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(6 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 9
-			Expect(s.Push(f3, 5*mult, cb3)).To(Succeed()) // 5 - 11
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f1,
-				6 * mult: f2,
-				9 * mult: f3[4*mult:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 11 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx---===-----
-		//     ++++++++++
-		// =>
-		// ---xxx++++++++---
-		It("case 31", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(10)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12
-			Expect(s.Push(f3, 5, cb3)).To(Succeed()) // 5 - 15
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f3[1:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 15, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 31, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 9))
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(3 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(10 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9*mult, cb2)).To(Succeed()) // 9 - 12
-			Expect(s.Push(f3, 5*mult, cb3)).To(Succeed()) // 5 - 15
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f1,
-				6 * mult: f3[mult:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 15 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx---===-----
-		//    +++++++++
-		// =>
-		// ---+++++++++---
-		It("case 32", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(9)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 12
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f3,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 12, End: protocol.MaxByteCount},
-			})
-			checkCallbackCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		// ---xxx---===###-----
-		//     ++++++++++++
-		// =>
-		// ---xxx++++++++++---
-		It("case 33", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(3)
-			cb2, t2 := getCallback()
-			f3 := getData(3)
-			cb3, t3 := getCallback()
-			f4 := getData(12)
-			cb4, t4 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12
-			Expect(s.Push(f3, 9, cb3)).To(Succeed()) // 12 - 15
-			Expect(s.Push(f4, 5, cb4)).To(Succeed()) // 5 - 17
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f1,
-				6: f4[1:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 17, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackCalled(t3)
-			checkCallbackCalled(t4)
-		})
-
-		It("case 33, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 11))
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(3 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(3 * mult)
-			cb3, t3 := getCallback()
-			f4 := getData(12 * mult)
-			cb4, t4 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9*mult, cb2)).To(Succeed()) // 9 - 12
-			Expect(s.Push(f3, 9*mult, cb3)).To(Succeed()) // 12 - 15
-			Expect(s.Push(f4, 5*mult, cb4)).To(Succeed()) // 5 - 17
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f1,
-				6 * mult: f4[mult:],
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 17 * mult, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackCalled(t3)
-			checkCallbackNotCalled(t4)
-		})
-
-		// ---xxx===---###
-		//       ++++++
-		// =>
-		// ---xxx++++++###
-		It("case 34", func() {
-			f1 := getData(5)
-			cb1, t1 := getCallback()
-			f2 := getData(5)
-			cb2, t2 := getCallback()
-			f3 := getData(10)
-			cb3, t3 := getCallback()
-			f4 := getData(5)
-			cb4, t4 := getCallback()
-			Expect(s.Push(f1, 5, cb1)).To(Succeed())  // 5 - 10
-			Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 - 15
-			Expect(s.Push(f4, 20, cb3)).To(Succeed()) // 20 - 25
-			Expect(s.Push(f3, 10, cb4)).To(Succeed()) // 10 - 20
-			checkQueue(map[protocol.ByteCount][]byte{
-				5:  f1,
-				10: f3,
-				20: f4,
-			})
-			checkGaps([]byteInterval{
-				{Start: 0, End: 5},
-				{Start: 25, End: protocol.MaxByteCount},
-			})
-			checkCallbackNotCalled(t1)
-			checkCallbackCalled(t2)
-			checkCallbackNotCalled(t3)
-			checkCallbackNotCalled(t4)
-		})
-
-		// ---xxx---####---
-		//    ++++++++
-		// =>
-		// ---++++++####---
-		It("case 35", func() {
-			f1 := getData(3)
-			cb1, t1 := getCallback()
-			f2 := getData(4)
-			cb2, t2 := getCallback()
-			f3 := getData(8)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 13
-			Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 11
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3},
-				{Start: 13, End: protocol.MaxByteCount},
-			})
-			checkQueue(map[protocol.ByteCount][]byte{
-				3: f3[:6],
-				9: f2,
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackCalled(t3)
-		})
-
-		It("case 35, for long frames", func() {
-			mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 6))
-			f1 := getData(3 * mult)
-			cb1, t1 := getCallback()
-			f2 := getData(4 * mult)
-			cb2, t2 := getCallback()
-			f3 := getData(8 * mult)
-			cb3, t3 := getCallback()
-			Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6
-			Expect(s.Push(f2, 9*mult, cb2)).To(Succeed()) // 9 - 13
-			Expect(s.Push(f3, 3*mult, cb3)).To(Succeed()) // 3 - 11
-			checkGaps([]byteInterval{
-				{Start: 0, End: 3 * mult},
-				{Start: 13 * mult, End: protocol.MaxByteCount},
-			})
-			checkQueue(map[protocol.ByteCount][]byte{
-				3 * mult: f3[:6*mult],
-				9 * mult: f2,
-			})
-			checkCallbackCalled(t1)
-			checkCallbackNotCalled(t2)
-			checkCallbackNotCalled(t3)
-		})
-
-		Context("receiving data after reads", func() {
-			It("ignores duplicate frames", func() {
-				Expect(s.Push([]byte("foobar"), 0, nil)).To(Succeed())
-				offset, data, _ := s.Pop()
-				Expect(offset).To(BeZero())
-				Expect(data).To(Equal([]byte("foobar")))
-				// now receive the duplicate
-				Expect(s.Push([]byte("foobar"), 0, nil)).To(Succeed())
-				Expect(s.queue).To(BeEmpty())
-				checkGaps([]byteInterval{
-					{Start: 6, End: protocol.MaxByteCount},
-				})
-			})
-
-			It("ignores parts of frames that have already been read", func() {
-				Expect(s.Push([]byte("foo"), 0, nil)).To(Succeed())
-				offset, data, _ := s.Pop()
-				Expect(offset).To(BeZero())
-				Expect(data).To(Equal([]byte("foo")))
-				// now receive the duplicate
-				Expect(s.Push([]byte("foobar"), 0, nil)).To(Succeed())
-				offset, data, _ = s.Pop()
-				Expect(offset).To(Equal(protocol.ByteCount(3)))
-				Expect(data).To(Equal([]byte("bar")))
-				Expect(s.queue).To(BeEmpty())
-				checkGaps([]byteInterval{
-					{Start: 6, End: protocol.MaxByteCount},
-				})
-			})
-		})
-
-		Context("DoS protection", func() {
-			It("errors when too many gaps are created", func() {
-				for i := 0; i < protocol.MaxStreamFrameSorterGaps; i++ {
-					Expect(s.Push([]byte("foobar"), protocol.ByteCount(i*7), nil)).To(Succeed())
-				}
-				Expect(s.gaps.Len()).To(Equal(protocol.MaxStreamFrameSorterGaps))
-				err := s.Push([]byte("foobar"), protocol.ByteCount(protocol.MaxStreamFrameSorterGaps*7)+100, nil)
-				Expect(err).To(MatchError("too many gaps in received data"))
-			})
+	// ---xxxx----=====-------
+	//      ++++++
+	// =>
+	// ---xxx++++=====--------
+	t.Run("case 11", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 7
+		require.NoError(t, s.Push(f2, 10, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f3, 5, cb3))  // 5 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			7:  f3[2:],
+			10: f2,
 		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
 	})
 
-	Context("stress testing", func() {
-		type frame struct {
-			offset protocol.ByteCount
-			data   []byte
-		}
+	// ---xxxx----=====-------
+	//      ++++++
+	// =>
+	// ---xxx++++=====--------
+	t.Run("case 11, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 3))
+		f1 := getData(4 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1))  // 3 - 7
+		require.NoError(t, s.Push(f2, 10*mult, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f3, 5*mult, cb3))  // 5 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult:  f1,
+			7 * mult:  f3[2*mult:],
+			10 * mult: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 15 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ----xxxx-------
+	//     +++++++
+	// =>
+	// ----+++++++-----
+	t.Run("case 12", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(7)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 7
+		require.NoError(t, s.Push(f2, 3, cb2)) // 3 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+	})
+
+	// ----xxx===-------
+	//     +++++++
+	// =>
+	// ----+++++++-----
+	t.Run("case 13", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(7)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 9
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-		for _, lf := range []bool{true, false} {
-			longFrames := lf
+	// ----xxx====-------
+	//     +++++
+	// =>
+	// ----+++====-----
+	t.Run("case 14", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 10
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f3[:3],
+			6: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
 
-			const num = 1000
+	t.Run("case 14, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 3))
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6*mult, cb2)) // 6 - 10
+		require.NoError(t, s.Push(f3, 3*mult, cb3)) // 3 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f3[:3*mult],
+			6 * mult: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 10 * mult, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-			name := "short"
-			if longFrames {
-				name = "long"
-			}
+	// ----xxx===-------
+	//     ++++++
+	// =>
+	// ----++++++-----
+	t.Run("case 15", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 9
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 9
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 9, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
 
-			Context(fmt.Sprintf("using %s frames", name), func() {
-				var data []byte
-				var dataLen protocol.ByteCount
-				var callbacks []callbackTracker
-
-				BeforeEach(func() {
-					seed := time.Now().UnixNano()
-					fmt.Fprintf(GinkgoWriter, "Seed: %d\n", seed)
-					rand.Seed(uint64(seed))
-
-					callbacks = nil
-					dataLen = 25
-					if longFrames {
-						dataLen = 2 * protocol.MinStreamFrameSize
-					}
-
-					data = make([]byte, num*dataLen)
-					for i := 0; i < num; i++ {
-						for j := protocol.ByteCount(0); j < dataLen; j++ {
-							data[protocol.ByteCount(i)*dataLen+j] = uint8(i)
-						}
-					}
-				})
-
-				getRandomFrames := func() []frame {
-					frames := make([]frame, num)
-					for i := protocol.ByteCount(0); i < num; i++ {
-						b := make([]byte, dataLen)
-						Expect(copy(b, data[i*dataLen:])).To(BeEquivalentTo(dataLen))
-						frames[i] = frame{
-							offset: i * dataLen,
-							data:   b,
-						}
-					}
-					rand.Shuffle(len(frames), func(i, j int) { frames[i], frames[j] = frames[j], frames[i] })
-					return frames
-				}
-
-				getData := func() []byte {
-					var data []byte
-					for {
-						offset, b, cb := s.Pop()
-						if b == nil {
-							break
-						}
-						Expect(offset).To(BeEquivalentTo(len(data)))
-						data = append(data, b...)
-						if cb != nil {
-							cb()
-						}
-					}
-					return data
-				}
-
-				// push pushes data to the frame sorter
-				// It creates a new callback and adds the
-				push := func(data []byte, offset protocol.ByteCount) {
-					cb, t := getCallback()
-					ExpectWithOffset(1, s.Push(data, offset, cb)).To(Succeed())
-					callbacks = append(callbacks, t)
-				}
-
-				checkCallbacks := func() {
-					ExpectWithOffset(1, callbacks).ToNot(BeEmpty())
-					for _, t := range callbacks {
-						checkCallbackCalled(t)
-					}
-				}
-
-				It("inserting frames in a random order", func() {
-					frames := getRandomFrames()
-
-					for _, f := range frames {
-						push(f.data, f.offset)
-					}
-					checkGaps([]byteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}})
-
-					Expect(getData()).To(Equal(data))
-					Expect(s.queue).To(BeEmpty())
-					checkCallbacks()
-				})
-
-				It("inserting frames in a random order, with some duplicates", func() {
-					frames := getRandomFrames()
-
-					for _, f := range frames {
-						push(f.data, f.offset)
-						if rand.Intn(10) < 5 {
-							df := frames[rand.Intn(len(frames))]
-							push(df.data, df.offset)
-						}
-					}
-					checkGaps([]byteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}})
-
-					Expect(getData()).To(Equal(data))
-					Expect(s.queue).To(BeEmpty())
-					checkCallbacks()
-				})
-
-				It("inserting frames in a random order, with randomly cut retransmissions", func() {
-					frames := getRandomFrames()
-
-					for _, f := range frames {
-						push(f.data, f.offset)
-						if rand.Intn(10) < 5 {
-							length := protocol.ByteCount(1 + rand.Intn(int(4*dataLen)))
-							if length >= num*dataLen {
-								length = num*dataLen - 1
-							}
-							b := make([]byte, length)
-							offset := protocol.ByteCount(rand.Intn(int(num*dataLen - length)))
-							Expect(copy(b, data[offset:offset+length])).To(BeEquivalentTo(length))
-							push(b, offset)
-						}
-					}
-					checkGaps([]byteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}})
-
-					Expect(getData()).To(Equal(data))
-					Expect(s.queue).To(BeEmpty())
-					checkCallbacks()
-				})
-			})
-		}
+	// ---xxxx-------
+	//    ++++
+	// =>
+	// ---xxxx-----
+	t.Run("case 16", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 9
+		require.NoError(t, s.Push(f2, 5, cb2)) // 5 - 9
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			5: f1,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 5},
+			{Start: 9, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+	})
+
+	// ----xxx===-------
+	//     +++
+	// =>
+	// ----xxx===-----
+	t.Run("case 17", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(3)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 9
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 6
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 9, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
+
+	// ---xxxx-------
+	//    ++
+	// =>
+	// ---xxxx-----
+	t.Run("case 18", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(4)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(2)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 9
+		require.NoError(t, s.Push(f2, 5, cb2)) // 5 - 7
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			5: f1,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 5},
+			{Start: 9, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+	})
+
+	// ---xxxxx------
+	//     ++
+	// =>
+	// ---xxxxx----
+	t.Run("case 19", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(5)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(2)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 10
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			5: f1,
+		})
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			5: f1,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 5},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+	})
+
+	// xxxxx------
+	//  ++
+	// =>
+	// xxxxx------
+	t.Run("case 20", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(10)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 0, cb1)) // 0 - 10
+		require.NoError(t, s.Push(f2, 5, cb2)) // 5 - 9
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			0: f1,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+	})
+	// ---xxxxx---
+	//      +++
+	// =>
+	// ---xxxxx---
+	t.Run("case 21", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(5)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 10
+		require.NoError(t, s.Push(f2, 7, cb2)) // 7 - 10
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 5},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			5: f1,
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+	})
+
+	// ----xxx------
+	//   +++++
+	// =>
+	// --+++++----
+	t.Run("case 22", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 8
+		require.NoError(t, s.Push(f2, 3, cb2)) // 3 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 8, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
 	})
-})
+
+	// ----xxx===------
+	//   ++++++++
+	// =>
+	// --++++++++----
+	t.Run("case 23", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(8)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 8
+		require.NoError(t, s.Push(f2, 8, cb2)) // 8 - 11
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 11, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// --xxx---===---
+	//      ++++++
+	// =>
+	// --xxx++++++----
+	t.Run("case 24", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9, cb2)) // 9 - 12
+		require.NoError(t, s.Push(f3, 6, cb3)) // 6 - 12
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 12, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// --xxx---===---###
+	//      +++++++++
+	// =>
+	// --xxx+++++++++###
+	t.Run("case 25", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(3)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		f4 := getData(9)
+		cb4, t4 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1))  // 3 - 6
+		require.NoError(t, s.Push(f2, 9, cb2))  // 9 - 12
+		require.NoError(t, s.Push(f3, 15, cb3)) // 15 - 18
+		require.NoError(t, s.Push(f4, 6, cb4))  // 6 - 15
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3:  f1,
+			6:  f4,
+			15: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 18, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+		require.False(t, t4.WasCalled())
+	})
+
+	// ----xxx------
+	//   +++++++
+	// =>
+	// --+++++++---
+	t.Run("case 26", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(10)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1)) // 5 - 8
+		require.NoError(t, s.Push(f2, 3, cb2)) // 3 - 13
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 13, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+	})
+
+	// ---xxx====---
+	//   ++++
+	// =>
+	// --+xxx====---
+	t.Run("case 27", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(4)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 10
+		require.NoError(t, s.Push(f3, 2, cb3)) // 2 - 6
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			2: f3[:1],
+			3: f1,
+			6: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 2},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
+
+	t.Run("case 27, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		const mult = protocol.MinStreamFrameSize
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(4 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6*mult, cb2)) // 6 - 10
+		require.NoError(t, s.Push(f3, 2*mult, cb3)) // 2 - 6
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			2 * mult: f3[:mult],
+			3 * mult: f1,
+			6 * mult: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 2 * mult},
+			{Start: 10 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ---xxx====---
+	//   ++++++
+	// =>
+	// --+xxx====---
+	t.Run("case 28", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 10
+		require.NoError(t, s.Push(f3, 2, cb3)) // 2 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			2: f3[:1],
+			3: f1,
+			6: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 2},
+			{Start: 10, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
+
+	t.Run("case 28, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		const mult = protocol.MinStreamFrameSize
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6*mult, cb2)) // 6 - 10
+		require.NoError(t, s.Push(f3, 2*mult, cb3)) // 2 - 8
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			2 * mult: f3[:mult],
+			3 * mult: f1,
+			6 * mult: f2,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 2 * mult},
+			{Start: 10 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ---xxx===-----
+	//       +++++
+	// =>
+	// ---xxx+++++---
+	t.Run("case 29", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(5)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 9
+		require.NoError(t, s.Push(f3, 6, cb3)) // 6 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 11, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ---xxx===----
+	//      ++++++
+	// =>
+	// ---xxx===++--
+	t.Run("case 30", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6, cb2)) // 6 - 9
+		require.NoError(t, s.Push(f3, 5, cb3)) // 5 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f2,
+			9: f3[4:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 11, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
+
+	t.Run("case 30, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2))
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(6 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 6*mult, cb2)) // 6 - 9
+		require.NoError(t, s.Push(f3, 5*mult, cb3)) // 5 - 11
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f1,
+			6 * mult: f2,
+			9 * mult: f3[4*mult:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 11 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ---xxx---===-----
+	//     ++++++++++
+	// =>
+	// ---xxx++++++++---
+	t.Run("case 31", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(10)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9, cb2)) // 9 - 12
+		require.NoError(t, s.Push(f3, 5, cb3)) // 5 - 15
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f3[1:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 15, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
+
+	t.Run("case 31, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 9))
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(10 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9*mult, cb2)) // 9 - 12
+		require.NoError(t, s.Push(f3, 5*mult, cb3)) // 5 - 15
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f1,
+			6 * mult: f3[mult:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 15 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ---xxx---===-----
+	//    +++++++++
+	// =>
+	// ---+++++++++---
+	t.Run("case 32", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(9)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9, cb2)) // 9 - 12
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 12
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f3,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 12, End: protocol.MaxByteCount},
+		})
+		require.True(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+
+	// ---xxx---===###-----
+	//     ++++++++++++
+	// =>
+	// ---xxx++++++++++---
+	t.Run("case 33", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(3)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		f4 := getData(12)
+		cb4, t4 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9, cb2)) // 9 - 12
+		require.NoError(t, s.Push(f3, 9, cb3)) // 12 - 15
+		require.NoError(t, s.Push(f4, 5, cb4)) // 5 - 17
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f1,
+			6: f4[1:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 17, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+		require.True(t, t4.WasCalled())
+	})
+
+	t.Run("case 33, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 11))
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(3 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(3 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		f4 := getData(12 * mult)
+		cb4, t4 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9*mult, cb2)) // 9 - 12
+		require.NoError(t, s.Push(f3, 9*mult, cb3)) // 12 - 15
+		require.NoError(t, s.Push(f4, 5*mult, cb4)) // 5 - 17
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f1,
+			6 * mult: f4[mult:],
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 17 * mult, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+		require.False(t, t4.WasCalled())
+	})
+
+	// ---xxx===---###
+	//       ++++++
+	// =>
+	// ---xxx++++++###
+	t.Run("case 34", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(5)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(5)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(10)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		f4 := getData(5)
+		cb4, t4 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 5, cb1))  // 5 - 10
+		require.NoError(t, s.Push(f2, 10, cb2)) // 10 - 15
+		require.NoError(t, s.Push(f4, 20, cb3)) // 20 - 25
+		require.NoError(t, s.Push(f3, 10, cb4)) // 10 - 20
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			5:  f1,
+			10: f3,
+			20: f4,
+		})
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 5},
+			{Start: 25, End: protocol.MaxByteCount},
+		})
+		require.False(t, t1.WasCalled())
+		require.True(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+		require.False(t, t4.WasCalled())
+	})
+
+	// ---xxx---####---
+	//    ++++++++
+	// =>
+	// ---++++++####---
+	t.Run("case 35", func(t *testing.T) {
+		s := newFrameSorter()
+		f1 := getData(3)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(8)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9, cb2)) // 9 - 13
+		require.NoError(t, s.Push(f3, 3, cb3)) // 3 - 11
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3},
+			{Start: 13, End: protocol.MaxByteCount},
+		})
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3: f3[:6],
+			9: f2,
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.True(t, t3.WasCalled())
+	})
+
+	t.Run("case 35, for long frames", func(t *testing.T) {
+		s := newFrameSorter()
+		mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 6))
+		f1 := getData(3 * mult)
+		cb1, t1 := getFrameSorterTestCallback(t)
+		f2 := getData(4 * mult)
+		cb2, t2 := getFrameSorterTestCallback(t)
+		f3 := getData(8 * mult)
+		cb3, t3 := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f1, 3*mult, cb1)) // 3 - 6
+		require.NoError(t, s.Push(f2, 9*mult, cb2)) // 9 - 13
+		require.NoError(t, s.Push(f3, 3*mult, cb3)) // 3 - 11
+		checkGaps(t, s, []byteInterval{
+			{Start: 0, End: 3 * mult},
+			{Start: 13 * mult, End: protocol.MaxByteCount},
+		})
+		checkQueue(t, s, map[protocol.ByteCount][]byte{
+			3 * mult: f3[:6*mult],
+			9 * mult: f2,
+		})
+		require.True(t, t1.WasCalled())
+		require.False(t, t2.WasCalled())
+		require.False(t, t3.WasCalled())
+	})
+}
+
+func TestFrameSorterTooManyGaps(t *testing.T) {
+	s := newFrameSorter()
+	for i := 0; i < protocol.MaxStreamFrameSorterGaps; i++ {
+		require.NoError(t, s.Push([]byte("foobar"), protocol.ByteCount(i*7), nil))
+	}
+	require.Equal(t, protocol.MaxStreamFrameSorterGaps, s.gaps.Len())
+	err := s.Push([]byte("foobar"), protocol.ByteCount(protocol.MaxStreamFrameSorterGaps*7)+100, nil)
+	require.EqualError(t, err, "too many gaps in received data")
+}
+
+func TestFrameSorterRandomized(t *testing.T) {
+	t.Run("short", func(t *testing.T) {
+		testFrameSorterRandomized(t, 25, false, false)
+	})
+	t.Run("long", func(t *testing.T) {
+		testFrameSorterRandomized(t, 2*protocol.MinStreamFrameSize, false, false)
+	})
+	t.Run("short, with duplicates", func(t *testing.T) {
+		testFrameSorterRandomized(t, 25, true, false)
+	})
+	t.Run("long, with duplicates", func(t *testing.T) {
+		testFrameSorterRandomized(t, 2*protocol.MinStreamFrameSize, true, false)
+	})
+	t.Run("short, with overlaps", func(t *testing.T) {
+		testFrameSorterRandomized(t, 25, false, true)
+	})
+	t.Run("long, with overlaps", func(t *testing.T) {
+		testFrameSorterRandomized(t, 2*protocol.MinStreamFrameSize, false, true)
+	})
+}
+
+func testFrameSorterRandomized(t *testing.T, dataLen protocol.ByteCount, injectDuplicates, injectOverlaps bool) {
+	type frame struct {
+		offset protocol.ByteCount
+		data   []byte
+	}
+
+	const num = 1000
+
+	data := make([]byte, num*int(dataLen))
+	rand.Read(data)
+
+	frames := make([]frame, num)
+	for i := 0; i < num; i++ {
+		b := make([]byte, dataLen)
+		offset := i * int(dataLen)
+		copy(b, data[offset:offset+int(dataLen)])
+		frames[i] = frame{
+			offset: protocol.ByteCount(i) * dataLen,
+			data:   b,
+		}
+	}
+	rand.Shuffle(len(frames), func(i, j int) { frames[i], frames[j] = frames[j], frames[i] })
+
+	s := newFrameSorter()
+
+	var callbacks []callbackTracker
+	for _, f := range frames {
+		cb, tr := getFrameSorterTestCallback(t)
+		require.NoError(t, s.Push(f.data, f.offset, cb))
+		callbacks = append(callbacks, tr)
+	}
+	if injectDuplicates {
+		for i := 0; i < num/10; i++ {
+			cb, tr := getFrameSorterTestCallback(t)
+			df := frames[rand.Intn(len(frames))]
+			require.NoError(t, s.Push(df.data, df.offset, cb))
+			callbacks = append(callbacks, tr)
+		}
+	}
+	if injectOverlaps {
+		finalOffset := num * dataLen
+		for i := 0; i < num/3; i++ {
+			cb, tr := getFrameSorterTestCallback(t)
+			startOffset := protocol.ByteCount(rand.Intn(int(finalOffset)))
+			endOffset := startOffset + protocol.ByteCount(rand.Intn(int(finalOffset-startOffset)))
+			require.NoError(t, s.Push(data[startOffset:endOffset], startOffset, cb))
+			callbacks = append(callbacks, tr)
+		}
+	}
+	require.Equal(t, 1, s.gaps.Len())
+	require.Equal(t, byteInterval{Start: num * dataLen, End: protocol.MaxByteCount}, s.gaps.Front().Value)
+
+	// read all data
+	var read []byte
+	for {
+		offset, b, cb := s.Pop()
+		if b == nil {
+			break
+		}
+		require.Equal(t, offset, protocol.ByteCount(len(read)))
+		read = append(read, b...)
+		if cb != nil {
+			cb()
+		}
+	}
+
+	require.Equal(t, data, read)
+	require.False(t, s.HasMoreData())
+	for _, cb := range callbacks {
+		require.True(t, cb.WasCalled())
+	}
+}
diff --git a/http3/transport_test.go b/http3/transport_test.go
index 8dce8cdf..9c4e06b0 100644
--- a/http3/transport_test.go
+++ b/http3/transport_test.go
@@ -333,9 +333,11 @@ var _ = Describe("Transport", func() {
 			cl2 := NewMockSingleRoundTripper(mockCtrl)
 			clientChan <- cl2
 
-			req1, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil)
+			ctx, cancel := context.WithCancel(context.Background())
+			req1, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://quic-go.net/foobar.html", nil)
 			Expect(err).ToNot(HaveOccurred())
-			req2, err := http.NewRequest("GET", "https://quic-go.net/bar.html", nil)
+
+			req2, err := http.NewRequest(http.MethodGet, "https://quic-go.net/bar.html", nil)
 			Expect(err).ToNot(HaveOccurred())
 
 			conn := mockquic.NewMockEarlyConnection(mockCtrl)
@@ -344,14 +346,18 @@ var _ = Describe("Transport", func() {
 				count++
 				return conn, nil
 			}
-			testErr := context.Canceled
 			handshakeChan := make(chan struct{})
 			close(handshakeChan)
 			conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2)
-			cl1.EXPECT().RoundTrip(req1).Return(nil, testErr)
+			cl1.EXPECT().RoundTrip(req1).DoAndReturn(
+				func(request *http.Request) (*http.Response, error) {
+					cancel()
+					return nil, context.Canceled
+				},
+			)
 			cl1.EXPECT().RoundTrip(req2).Return(&http.Response{Request: req2}, nil)
 			_, err = tr.RoundTrip(req1)
-			Expect(err).To(MatchError(testErr))
+			Expect(err).To(MatchError(context.Canceled))
 			rsp, err := tr.RoundTrip(req2)
 			Expect(err).ToNot(HaveOccurred())
 			Expect(rsp.Request).To(Equal(req2))
diff --git a/integrationtests/self/close_test.go b/integrationtests/self/close_test.go
index b03b72ea..583a804d 100644
--- a/integrationtests/self/close_test.go
+++ b/integrationtests/self/close_test.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/quic-go/quic-go"
 	quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
+	"github.com/quic-go/quic-go/internal/protocol"
 
 	"github.com/stretchr/testify/require"
 )
@@ -79,3 +80,36 @@ func TestConnectionCloseRetransmission(t *testing.T) {
 		require.Equal(t, packets[0], packets[i])
 	}
 }
+
+func TestDrainServerAcceptQueue(t *testing.T) {
+	server, err := quic.Listen(newUPDConnLocalhost(t), getTLSConfig(), getQuicConfig(nil))
+	require.NoError(t, err)
+	defer server.Close()
+
+	dialer := &quic.Transport{Conn: newUPDConnLocalhost(t)}
+	defer dialer.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+	defer cancel()
+	// fill up the accept queue
+	conns := make([]quic.Connection, 0, protocol.MaxAcceptQueueSize)
+	for i := 0; i < protocol.MaxAcceptQueueSize; i++ {
+		conn, err := dialer.Dial(ctx, server.Addr(), getTLSClientConfig(), getQuicConfig(nil))
+		require.NoError(t, err)
+		conns = append(conns, conn)
+	}
+	time.Sleep(scaleDuration(25 * time.Millisecond)) // wait for connections to be queued
+
+	server.Close()
+	for i := range protocol.MaxAcceptQueueSize {
+		c, err := server.Accept(ctx)
+		require.NoError(t, err)
+		// make sure the connection is not closed
+		require.NoError(t, conns[i].Context().Err(), "client connection closed")
+		require.NoError(t, c.Context().Err(), "server connection closed")
+		conns[i].CloseWithError(0, "")
+		c.CloseWithError(0, "")
+	}
+	_, err = server.Accept(ctx)
+	require.ErrorIs(t, err, quic.ErrServerClosed)
+}
diff --git a/multiplexer_test.go b/multiplexer_test.go
deleted file mode 100644
index b98dc92c..00000000
--- a/multiplexer_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package quic
-
-import (
-	"net"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-type mockIndexableConn struct{ addr net.Addr }
-
-var _ indexableConn = &mockIndexableConn{}
-
-func (m *mockIndexableConn) LocalAddr() net.Addr { return m.addr }
-
-func TestMultiplexerAddNewPacketConns(t *testing.T) {
-	conn1 := &mockIndexableConn{addr: &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}}
-	getMultiplexer().AddConn(conn1)
-	conn2 := &mockIndexableConn{addr: &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1235}}
-	getMultiplexer().AddConn(conn2)
-
-	require.NoError(t, getMultiplexer().RemoveConn(conn1))
-	require.NoError(t, getMultiplexer().RemoveConn(conn2))
-}
-
-func TestMultiplexerPanicsOnDuplicateConn(t *testing.T) {
-	conn := &mockIndexableConn{addr: &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 4321}}
-	getMultiplexer().AddConn(conn)
-	require.Panics(t, func() { getMultiplexer().AddConn(conn) })
-
-	require.NoError(t, getMultiplexer().RemoveConn(conn))
-	require.ErrorContains(t, getMultiplexer().RemoveConn(conn), "cannote remove connection")
-}
diff --git a/quic_suite_test.go b/quic_suite_test.go
index 4744b7c3..17cc1878 100644
--- a/quic_suite_test.go
+++ b/quic_suite_test.go
@@ -10,7 +10,6 @@ import (
 	"runtime/pprof"
 	"strconv"
 	"strings"
-	"sync"
 	"testing"
 	"time"
 
@@ -29,9 +28,6 @@ var mockCtrl *gomock.Controller
 
 var _ = BeforeEach(func() {
 	mockCtrl = gomock.NewController(GinkgoT())
-
-	// reset the sync.Once
-	connMuxerOnce = *new(sync.Once)
 })
 
 var _ = BeforeSuite(func() {
diff --git a/transport_test.go b/transport_test.go
index a114fa27..ccbebd34 100644
--- a/transport_test.go
+++ b/transport_test.go
@@ -416,9 +416,6 @@ func TestTransportFaultySyscallConn(t *testing.T) {
 	_, err := tr.Listen(&tls.Config{}, nil)
 	require.Error(t, err)
 	require.ErrorContains(t, err, "mocked")
-
-	conns := getMultiplexer().(*connMultiplexer).conns
-	require.Empty(t, conns)
 }
 
 func TestTransportSetTLSConfigServerName(t *testing.T) {