From 3ffe961079477b1958079e822450f97333ab099f Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 6 Jan 2025 16:17:50 +0800 Subject: [PATCH] migrate the crypto stream tests away from Ginkgo (#4837) --- crypto_stream_test.go | 227 ++++++++++++++++-------------------------- 1 file changed, 85 insertions(+), 142 deletions(-) diff --git a/crypto_stream_test.go b/crypto_stream_test.go index 7a5eb9dfc..6b5488825 100644 --- a/crypto_stream_test.go +++ b/crypto_stream_test.go @@ -1,158 +1,101 @@ package quic import ( - "fmt" + "testing" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/require" ) -var _ = Describe("Crypto Stream", func() { - var str *cryptoStream +func TestCryptoStreamDataAssembly(t *testing.T) { + str := newCryptoStream() + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3})) + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})) + // receive a retransmission + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3})) - BeforeEach(func() { str = newCryptoStream() }) + var data []byte + for { + b := str.GetCryptoData() + if b == nil { + break + } + data = append(data, b...) + } + require.Equal(t, []byte("foobar"), data) +} - Context("handling incoming data", func() { - It("handles in-order CRYPTO frames", func() { - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})).To(Succeed()) - Expect(str.GetCryptoData()).To(Equal([]byte("foo"))) - Expect(str.GetCryptoData()).To(BeNil()) - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3})).To(Succeed()) - Expect(str.GetCryptoData()).To(Equal([]byte("bar"))) - Expect(str.GetCryptoData()).To(BeNil()) - }) +func TestCryptoStreamMaxOffset(t *testing.T) { + str := newCryptoStream() + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{ + Offset: protocol.MaxCryptoStreamOffset - 5, + Data: []byte("foo"), + })) + require.ErrorIs(t, + str.HandleCryptoFrame(&wire.CryptoFrame{ + Offset: protocol.MaxCryptoStreamOffset - 2, + Data: []byte("bar"), + }), + &qerr.TransportError{ErrorCode: qerr.CryptoBufferExceeded}, + ) +} - It("errors if the frame exceeds the maximum offset", func() { - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ - Offset: protocol.MaxCryptoStreamOffset - 5, - Data: []byte("foobar"), - })).To(MatchError(&qerr.TransportError{ - ErrorCode: qerr.CryptoBufferExceeded, - ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", protocol.MaxCryptoStreamOffset+1, protocol.MaxCryptoStreamOffset), - })) - }) - - It("handles out-of-order CRYPTO frames", func() { - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Offset: 3, Data: []byte("bar")})).To(Succeed()) - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})).To(Succeed()) - var data []byte - for { - b := str.GetCryptoData() - if b == nil { - break - } - data = append(data, b...) - } - Expect(data).To(Equal([]byte("foobar"))) - Expect(str.GetCryptoData()).To(BeNil()) - }) - - Context("finishing", func() { - It("errors if there's still data to read at the current offset after finishing", func() { - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ - Data: []byte("foo"), - })).To(Succeed()) - Expect(str.Finish()).To(MatchError(&qerr.TransportError{ - ErrorCode: qerr.ProtocolViolation, - ErrorMessage: "encryption level changed, but crypto stream has more data to read", - })) - }) - - It("errors if there's still data to read at a higher offset after finishing", func() { - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ - Data: []byte("foobar"), - Offset: 10, - })).To(Succeed()) - Expect(str.Finish()).To(MatchError(&qerr.TransportError{ - ErrorCode: qerr.ProtocolViolation, - ErrorMessage: "encryption level changed, but crypto stream has more data to read", - })) - }) - - It("works with reordered data", func() { - f1 := &wire.CryptoFrame{ - Data: []byte("foo"), - } - f2 := &wire.CryptoFrame{ - Offset: 3, - Data: []byte("bar"), - } - Expect(str.HandleCryptoFrame(f2)).To(Succeed()) - Expect(str.HandleCryptoFrame(f1)).To(Succeed()) - Expect(str.GetCryptoData()).To(HaveLen(3)) - Expect(str.GetCryptoData()).To(HaveLen(3)) - Expect(str.Finish()).To(Succeed()) - Expect(str.HandleCryptoFrame(f2)).To(Succeed()) - }) - - It("rejects new crypto data after finishing", func() { - Expect(str.Finish()).To(Succeed()) - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ - Data: []byte("foo"), - })).To(MatchError(&qerr.TransportError{ - ErrorCode: qerr.ProtocolViolation, - ErrorMessage: "received crypto data after change of encryption level", - })) - }) - - It("ignores crypto data below the maximum offset received before finishing", func() { - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ - Data: []byte("foobar"), - })).To(Succeed()) - Expect(str.GetCryptoData()).To(Equal([]byte("foobar"))) - Expect(str.Finish()).To(Succeed()) - Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ - Offset: 2, - Data: []byte("foo"), - })).To(Succeed()) - }) - }) +func TestCryptoStreamFinishWithQueuedData(t *testing.T) { + t.Run("with data at current offset", func(t *testing.T) { + str := newCryptoStream() + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})) + require.Equal(t, []byte("foo"), str.GetCryptoData()) + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3})) + require.ErrorIs(t, str.Finish(), &qerr.TransportError{ErrorCode: qerr.ProtocolViolation}) }) - Context("writing data", func() { - It("says if it has data", func() { - Expect(str.HasData()).To(BeFalse()) - _, err := str.Write([]byte("foobar")) - Expect(err).ToNot(HaveOccurred()) - Expect(str.HasData()).To(BeTrue()) - }) - - It("pops crypto frames", func() { - _, err := str.Write([]byte("foobar")) - Expect(err).ToNot(HaveOccurred()) - f := str.PopCryptoFrame(1000) - Expect(f).ToNot(BeNil()) - Expect(f.Offset).To(BeZero()) - Expect(f.Data).To(Equal([]byte("foobar"))) - }) - - It("coalesces multiple writes", func() { - _, err := str.Write([]byte("foo")) - Expect(err).ToNot(HaveOccurred()) - _, err = str.Write([]byte("bar")) - Expect(err).ToNot(HaveOccurred()) - f := str.PopCryptoFrame(1000) - Expect(f).ToNot(BeNil()) - Expect(f.Offset).To(BeZero()) - Expect(f.Data).To(Equal([]byte("foobar"))) - }) - - It("respects the maximum size", func() { - frameHeaderLen := (&wire.CryptoFrame{}).Length(protocol.Version1) - _, err := str.Write([]byte("foobar")) - Expect(err).ToNot(HaveOccurred()) - f := str.PopCryptoFrame(frameHeaderLen + 3) - Expect(f).ToNot(BeNil()) - Expect(f.Offset).To(BeZero()) - Expect(f.Data).To(Equal([]byte("foo"))) - f = str.PopCryptoFrame(frameHeaderLen + 3) - Expect(f).ToNot(BeNil()) - Expect(f.Offset).To(Equal(protocol.ByteCount(3))) - Expect(f.Data).To(Equal([]byte("bar"))) - }) + t.Run("with data at a higher offset", func(t *testing.T) { + str := newCryptoStream() + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foobar"), Offset: 20})) + require.ErrorIs(t, str.Finish(), &qerr.TransportError{ErrorCode: qerr.ProtocolViolation}) }) -}) +} + +func TestCryptoStreamReceiveDataAfterFinish(t *testing.T) { + str := newCryptoStream() + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foobar")})) + require.Equal(t, []byte("foobar"), str.GetCryptoData()) + require.NoError(t, str.Finish()) + // receiving a retransmission is ok + require.NoError(t, str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3})) + // but receiving new data is not + require.ErrorIs(t, + str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("baz"), Offset: 4}), + &qerr.TransportError{ErrorCode: qerr.ProtocolViolation}, + ) +} + +func TestCryptoStreamWrite(t *testing.T) { + expectedCryptoFrameLen := func(offset protocol.ByteCount) protocol.ByteCount { + f := &wire.CryptoFrame{Offset: offset} + return f.Length(protocol.Version1) + } + + str := newCryptoStream() + + require.False(t, str.HasData()) + _, err := str.Write([]byte("foo")) + require.NoError(t, err) + require.True(t, str.HasData()) + _, err = str.Write([]byte("bar")) + require.NoError(t, err) + _, err = str.Write([]byte("baz")) + require.NoError(t, err) + require.True(t, str.HasData()) + + f := str.PopCryptoFrame(expectedCryptoFrameLen(0) + 3) + require.Equal(t, &wire.CryptoFrame{Data: []byte("foo")}, f) + require.True(t, str.HasData()) + f = str.PopCryptoFrame(protocol.MaxByteCount) + // the two write calls were coalesced into a single frame + require.Equal(t, &wire.CryptoFrame{Offset: 3, Data: []byte("barbaz")}, f) + require.False(t, str.HasData()) +}