From 79e56808900faf649d4e372870c95a164374554a Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 19 Apr 2025 20:47:17 +0800 Subject: [PATCH] migrate the retransmission queue tests away from Ginkgo (#5054) --- retransmission_queue_test.go | 308 +++++++++++++---------------------- 1 file changed, 116 insertions(+), 192 deletions(-) diff --git a/retransmission_queue_test.go b/retransmission_queue_test.go index 8a1c0cdb7..0dd12a3f3 100644 --- a/retransmission_queue_test.go +++ b/retransmission_queue_test.go @@ -1,207 +1,131 @@ package quic import ( + "testing" + "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/require" ) -var _ = Describe("Retransmission queue", func() { - var q *retransmissionQueue - - BeforeEach(func() { - q = newRetransmissionQueue() +func TestRetransmissionQueueFrames(t *testing.T) { + t.Run("Initial", func(t *testing.T) { + testRetransmissionQueueFrames(t, protocol.EncryptionInitial) }) - - Context("Initial data", func() { - It("doesn't dequeue anything when it's empty", func() { - Expect(q.HasData(protocol.EncryptionInitial)).To(BeFalse()) - Expect(q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1)).To(BeNil()) - }) - - It("queues and retrieves a control frame", func() { - f := &wire.MaxDataFrame{MaximumData: 0x42} - q.addInitial(f) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionInitial, f.Length(protocol.Version1)-1, protocol.Version1)).To(BeNil()) - Expect(q.GetFrame(protocol.EncryptionInitial, f.Length(protocol.Version1), protocol.Version1)).To(Equal(f)) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeFalse()) - }) - - It("queues and retrieves a CRYPTO frame", func() { - f := &wire.CryptoFrame{Data: []byte("foobar")} - q.addInitial(f) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionInitial, f.Length(protocol.Version1), protocol.Version1)).To(Equal(f)) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeFalse()) - }) - - It("returns split CRYPTO frames", func() { - f := &wire.CryptoFrame{ - Offset: 100, - Data: []byte("foobar"), - } - q.addInitial(f) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - f1 := q.GetFrame(protocol.EncryptionInitial, f.Length(protocol.Version1)-3, protocol.Version1) - Expect(f1).ToNot(BeNil()) - Expect(f1).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) - Expect(f1.(*wire.CryptoFrame).Data).To(Equal([]byte("foo"))) - Expect(f1.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(100))) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - f2 := q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1) - Expect(f2).ToNot(BeNil()) - Expect(f2).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) - Expect(f2.(*wire.CryptoFrame).Data).To(Equal([]byte("bar"))) - Expect(f2.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(103))) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeFalse()) - }) - - It("returns other frames when a CRYPTO frame wouldn't fit", func() { - f := &wire.CryptoFrame{Data: []byte("foobar")} - q.addInitial(f) - q.addInitial(&wire.PingFrame{}) - f1 := q.GetFrame(protocol.EncryptionInitial, 2, protocol.Version1) // too small for a CRYPTO frame - Expect(f1).ToNot(BeNil()) - Expect(f1).To(BeAssignableToTypeOf(&wire.PingFrame{})) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - f2 := q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1) - Expect(f2).To(Equal(f)) - }) - - It("retrieves both a CRYPTO frame and a control frame", func() { - cf := &wire.MaxDataFrame{MaximumData: 0x42} - f := &wire.CryptoFrame{Data: []byte("foobar")} - q.addInitial(f) - q.addInitial(cf) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1)).To(Equal(f)) - Expect(q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1)).To(Equal(cf)) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeFalse()) - }) - - It("drops all Initial frames", func() { - q.addInitial(&wire.CryptoFrame{Data: []byte("foobar")}) - q.addInitial(&wire.MaxDataFrame{MaximumData: 0x42}) - q.DropPackets(protocol.EncryptionInitial) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeFalse()) - Expect(q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1)).To(BeNil()) - }) - - It("retransmits a frame", func() { - f := &wire.MaxDataFrame{MaximumData: 0x42} - q.AckHandler(protocol.EncryptionInitial).OnLost(f) - Expect(q.HasData(protocol.EncryptionInitial)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionInitial, protocol.MaxByteCount, protocol.Version1)).To(Equal(f)) - }) + t.Run("Handshake", func(t *testing.T) { + testRetransmissionQueueFrames(t, protocol.EncryptionHandshake) }) - - Context("Handshake data", func() { - It("doesn't dequeue anything when it's empty", func() { - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeFalse()) - Expect(q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1)).To(BeNil()) - }) - - It("queues and retrieves a control frame", func() { - f := &wire.MaxDataFrame{MaximumData: 0x42} - q.addHandshake(f) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionHandshake, f.Length(protocol.Version1)-1, protocol.Version1)).To(BeNil()) - Expect(q.GetFrame(protocol.EncryptionHandshake, f.Length(protocol.Version1), protocol.Version1)).To(Equal(f)) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeFalse()) - }) - - It("queues and retrieves a CRYPTO frame", func() { - f := &wire.CryptoFrame{Data: []byte("foobar")} - q.addHandshake(f) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionHandshake, f.Length(protocol.Version1), protocol.Version1)).To(Equal(f)) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeFalse()) - }) - - It("returns split CRYPTO frames", func() { - f := &wire.CryptoFrame{ - Offset: 100, - Data: []byte("foobar"), - } - q.addHandshake(f) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - f1 := q.GetFrame(protocol.EncryptionHandshake, f.Length(protocol.Version1)-3, protocol.Version1) - Expect(f1).ToNot(BeNil()) - Expect(f1).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) - Expect(f1.(*wire.CryptoFrame).Data).To(Equal([]byte("foo"))) - Expect(f1.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(100))) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - f2 := q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1) - Expect(f2).ToNot(BeNil()) - Expect(f2).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) - Expect(f2.(*wire.CryptoFrame).Data).To(Equal([]byte("bar"))) - Expect(f2.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(103))) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeFalse()) - }) - - It("returns other frames when a CRYPTO frame wouldn't fit", func() { - f := &wire.CryptoFrame{Data: []byte("foobar")} - q.addHandshake(f) - q.addHandshake(&wire.PingFrame{}) - f1 := q.GetFrame(protocol.EncryptionHandshake, 2, protocol.Version1) // too small for a CRYPTO frame - Expect(f1).ToNot(BeNil()) - Expect(f1).To(BeAssignableToTypeOf(&wire.PingFrame{})) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - f2 := q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1) - Expect(f2).To(Equal(f)) - }) - - It("retrieves both a CRYPTO frame and a control frame", func() { - cf := &wire.MaxDataFrame{MaximumData: 0x42} - f := &wire.CryptoFrame{Data: []byte("foobar")} - q.addHandshake(f) - q.addHandshake(cf) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1)).To(Equal(f)) - Expect(q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1)).To(Equal(cf)) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeFalse()) - }) - - It("drops all Handshake frames", func() { - q.addHandshake(&wire.CryptoFrame{Data: []byte("foobar")}) - q.addHandshake(&wire.MaxDataFrame{MaximumData: 0x42}) - q.DropPackets(protocol.EncryptionHandshake) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeFalse()) - Expect(q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1)).To(BeNil()) - }) - - It("retransmits a frame", func() { - f := &wire.MaxDataFrame{MaximumData: 0x42} - q.AckHandler(protocol.EncryptionHandshake).OnLost(f) - Expect(q.HasData(protocol.EncryptionHandshake)).To(BeTrue()) - Expect(q.GetFrame(protocol.EncryptionHandshake, protocol.MaxByteCount, protocol.Version1)).To(Equal(f)) - }) + t.Run("1-RTT", func(t *testing.T) { + testRetransmissionQueueFrames(t, protocol.Encryption1RTT) }) +} - Context("Application data", func() { - It("doesn't dequeue anything when it's empty", func() { - Expect(q.HasData(protocol.Encryption1RTT)).To(BeFalse()) - Expect(q.GetFrame(protocol.Encryption1RTT, protocol.MaxByteCount, protocol.Version1)).To(BeNil()) - }) +func testRetransmissionQueueFrames(t *testing.T, encLevel protocol.EncryptionLevel) { + q := newRetransmissionQueue() - It("queues and retrieves a control frame", func() { - f := &wire.MaxDataFrame{MaximumData: 0x42} - Expect(q.HasData(protocol.Encryption1RTT)).To(BeFalse()) - q.addAppData(f) - Expect(q.HasData(protocol.Encryption1RTT)).To(BeTrue()) - Expect(q.GetFrame(protocol.Encryption1RTT, f.Length(protocol.Version1)-1, protocol.Version1)).To(BeNil()) - Expect(q.GetFrame(protocol.Encryption1RTT, f.Length(protocol.Version1), protocol.Version1)).To(Equal(f)) - Expect(q.HasData(protocol.Encryption1RTT)).To(BeFalse()) - }) + require.False(t, q.HasData(encLevel)) + require.Nil(t, q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1)) - It("retransmits a frame", func() { - f := &wire.MaxDataFrame{MaximumData: 0x42} - q.AckHandler(protocol.Encryption1RTT).OnLost(f) - Expect(q.HasData(protocol.Encryption1RTT)).To(BeTrue()) - Expect(q.GetFrame(protocol.Encryption1RTT, protocol.MaxByteCount, protocol.Version1)).To(Equal(f)) - }) + ah := q.AckHandler(encLevel) + require.NotNil(t, ah) + ah.OnLost(&wire.PingFrame{}) + require.True(t, q.HasData(encLevel)) + require.Equal(t, &wire.PingFrame{}, q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1)) + require.False(t, q.HasData(encLevel)) + require.Nil(t, q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1)) + + f := &wire.PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} + ah.OnLost(f) + require.True(t, q.HasData(encLevel)) + require.Nil(t, q.GetFrame(encLevel, f.Length(protocol.Version1)-1, protocol.Version1)) + require.Equal(t, f, q.GetFrame(encLevel, f.Length(protocol.Version1), protocol.Version1)) + require.False(t, q.HasData(encLevel)) + + if encLevel == protocol.Encryption1RTT { + require.Panics(t, func() { ah.OnLost(&wire.StreamFrame{}) }) + } +} + +func TestRetransmissionQueueCryptoFrames(t *testing.T) { + t.Run("Initial", func(t *testing.T) { + testRetransmissionQueueCryptoFrames(t, protocol.EncryptionInitial) }) -}) + t.Run("Handshake", func(t *testing.T) { + testRetransmissionQueueCryptoFrames(t, protocol.EncryptionHandshake) + }) + t.Run("1-RTT", func(t *testing.T) { + testRetransmissionQueueCryptoFrames(t, protocol.Encryption1RTT) + }) +} + +func testRetransmissionQueueCryptoFrames(t *testing.T, encLevel protocol.EncryptionLevel) { + q := newRetransmissionQueue() + + var otherEncLevel protocol.EncryptionLevel + switch encLevel { + case protocol.EncryptionInitial: + otherEncLevel = protocol.EncryptionHandshake + case protocol.EncryptionHandshake: + otherEncLevel = protocol.Encryption1RTT + case protocol.Encryption1RTT: + otherEncLevel = protocol.EncryptionInitial + } + + ah := q.AckHandler(encLevel) + require.NotNil(t, ah) + ah.OnLost(&wire.CryptoFrame{Data: []byte("foobar")}) + require.True(t, q.HasData(encLevel)) + require.False(t, q.HasData(otherEncLevel)) + require.Equal(t, &wire.CryptoFrame{Data: []byte("foobar")}, q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1)) + require.False(t, q.HasData(encLevel)) + require.Nil(t, q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1)) + + f := &wire.CryptoFrame{Offset: 100, Data: []byte("foobar")} + ah.OnLost(f) + ah.OnLost(&wire.PingFrame{}) + require.True(t, q.HasData(encLevel)) + require.False(t, q.HasData(otherEncLevel)) + // the CRYPTO frame wouldn't fit, not even if it was split + require.IsType(t, &wire.PingFrame{}, q.GetFrame(encLevel, 2, protocol.Version1)) + + f1 := q.GetFrame(encLevel, f.Length(protocol.Version1)-3, protocol.Version1) + require.NotNil(t, f1) + require.IsType(t, &wire.CryptoFrame{}, f1) + require.Equal(t, &wire.CryptoFrame{Offset: 100, Data: []byte("foo")}, f1) + f2 := q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1) + require.NotNil(t, f2) + require.IsType(t, &wire.CryptoFrame{}, f2) + require.Equal(t, &wire.CryptoFrame{Offset: 103, Data: []byte("bar")}, f2) +} + +func TestRetransmissionQueueDropEncLevel(t *testing.T) { + q := newRetransmissionQueue() + require.Panics(t, func() { q.DropPackets(protocol.Encryption0RTT) }) + require.Panics(t, func() { q.DropPackets(protocol.Encryption1RTT) }) + + t.Run("Initial", func(t *testing.T) { + testRetransmissionQueueDropEncLevel(t, protocol.EncryptionInitial) + }) + t.Run("Handshake", func(t *testing.T) { + testRetransmissionQueueDropEncLevel(t, protocol.EncryptionHandshake) + }) +} + +func testRetransmissionQueueDropEncLevel(t *testing.T, encLevel protocol.EncryptionLevel) { + q := newRetransmissionQueue() + + ah := q.AckHandler(encLevel) + require.NotNil(t, ah) + ah.OnLost(&wire.PingFrame{}) + ah.OnLost(&wire.CryptoFrame{Data: []byte("foobar")}) + require.True(t, q.HasData(encLevel)) + q.DropPackets(encLevel) + require.False(t, q.HasData(encLevel)) + require.Nil(t, q.GetFrame(encLevel, protocol.MaxByteCount, protocol.Version1)) + + // losing more frame is a no-op + ah.OnLost(&wire.CryptoFrame{Data: []byte("foobar")}) + ah.OnLost(&wire.PingFrame{}) + require.False(t, q.HasData(encLevel)) +}