github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/congestion/cubic_sender_test.go (about)

     1  package congestion
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/daeuniverse/quic-go/internal/protocol"
     7  	"github.com/daeuniverse/quic-go/internal/utils"
     8  
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  )
    12  
    13  const (
    14  	initialCongestionWindowPackets = 10
    15  	defaultWindowTCP               = protocol.ByteCount(initialCongestionWindowPackets) * maxDatagramSize
    16  )
    17  
    18  type mockClock time.Time
    19  
    20  func (c *mockClock) Now() time.Time {
    21  	return time.Time(*c)
    22  }
    23  
    24  func (c *mockClock) Advance(d time.Duration) {
    25  	*c = mockClock(time.Time(*c).Add(d))
    26  }
    27  
    28  const MaxCongestionWindow protocol.ByteCount = 200 * maxDatagramSize
    29  
    30  var _ = Describe("Cubic Sender", func() {
    31  	var (
    32  		sender            *cubicSender
    33  		clock             mockClock
    34  		bytesInFlight     protocol.ByteCount
    35  		packetNumber      protocol.PacketNumber
    36  		ackedPacketNumber protocol.PacketNumber
    37  		rttStats          *utils.RTTStats
    38  	)
    39  
    40  	BeforeEach(func() {
    41  		bytesInFlight = 0
    42  		packetNumber = 1
    43  		ackedPacketNumber = 0
    44  		clock = mockClock{}
    45  		rttStats = utils.NewRTTStats()
    46  		sender = newCubicSender(
    47  			&clock,
    48  			rttStats,
    49  			true, /*reno*/
    50  			protocol.InitialPacketSizeIPv4,
    51  			initialCongestionWindowPackets*maxDatagramSize,
    52  			MaxCongestionWindow,
    53  			nil,
    54  		)
    55  	})
    56  
    57  	SendAvailableSendWindowLen := func(packetLength protocol.ByteCount) int {
    58  		var packetsSent int
    59  		for sender.CanSend(bytesInFlight) {
    60  			sender.OnPacketSent(clock.Now(), bytesInFlight, packetNumber, packetLength, true)
    61  			packetNumber++
    62  			packetsSent++
    63  			bytesInFlight += packetLength
    64  		}
    65  		return packetsSent
    66  	}
    67  
    68  	// Normal is that TCP acks every other segment.
    69  	AckNPackets := func(n int) {
    70  		rttStats.UpdateRTT(60*time.Millisecond, 0, clock.Now())
    71  		sender.MaybeExitSlowStart()
    72  		for i := 0; i < n; i++ {
    73  			ackedPacketNumber++
    74  			sender.OnPacketAcked(ackedPacketNumber, maxDatagramSize, bytesInFlight, clock.Now())
    75  		}
    76  		bytesInFlight -= protocol.ByteCount(n) * maxDatagramSize
    77  		clock.Advance(time.Millisecond)
    78  	}
    79  
    80  	LoseNPacketsLen := func(n int, packetLength protocol.ByteCount) {
    81  		for i := 0; i < n; i++ {
    82  			ackedPacketNumber++
    83  			sender.OnCongestionEvent(ackedPacketNumber, packetLength, bytesInFlight)
    84  		}
    85  		bytesInFlight -= protocol.ByteCount(n) * packetLength
    86  	}
    87  
    88  	// Does not increment acked_packet_number_.
    89  	LosePacket := func(number protocol.PacketNumber) {
    90  		sender.OnCongestionEvent(number, maxDatagramSize, bytesInFlight)
    91  		bytesInFlight -= maxDatagramSize
    92  	}
    93  
    94  	SendAvailableSendWindow := func() int { return SendAvailableSendWindowLen(maxDatagramSize) }
    95  	LoseNPackets := func(n int) { LoseNPacketsLen(n, maxDatagramSize) }
    96  
    97  	It("has the right values at startup", func() {
    98  		// At startup make sure we are at the default.
    99  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   100  		// Make sure we can send.
   101  		Expect(sender.TimeUntilSend(0)).To(BeZero())
   102  		Expect(sender.CanSend(bytesInFlight)).To(BeTrue())
   103  		// And that window is un-affected.
   104  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   105  
   106  		// Fill the send window with data, then verify that we can't send.
   107  		SendAvailableSendWindow()
   108  		Expect(sender.CanSend(bytesInFlight)).To(BeFalse())
   109  	})
   110  
   111  	It("paces", func() {
   112  		rttStats.UpdateRTT(10*time.Millisecond, 0, time.Now())
   113  		clock.Advance(time.Hour)
   114  		// Fill the send window with data, then verify that we can't send.
   115  		SendAvailableSendWindow()
   116  		AckNPackets(1)
   117  		delay := sender.TimeUntilSend(bytesInFlight)
   118  		Expect(delay).ToNot(BeZero())
   119  		Expect(delay).ToNot(Equal(utils.InfDuration))
   120  	})
   121  
   122  	It("application limited slow start", func() {
   123  		// Send exactly 10 packets and ensure the CWND ends at 14 packets.
   124  		const numberOfAcks = 5
   125  		// At startup make sure we can send.
   126  		Expect(sender.CanSend(0)).To(BeTrue())
   127  		Expect(sender.TimeUntilSend(0)).To(BeZero())
   128  
   129  		SendAvailableSendWindow()
   130  		for i := 0; i < numberOfAcks; i++ {
   131  			AckNPackets(2)
   132  		}
   133  		bytesToSend := sender.GetCongestionWindow()
   134  		// It's expected 2 acks will arrive when the bytes_in_flight are greater than
   135  		// half the CWND.
   136  		Expect(bytesToSend).To(Equal(defaultWindowTCP + maxDatagramSize*2*2))
   137  	})
   138  
   139  	It("exponential slow start", func() {
   140  		const numberOfAcks = 20
   141  		// At startup make sure we can send.
   142  		Expect(sender.CanSend(0)).To(BeTrue())
   143  		Expect(sender.TimeUntilSend(0)).To(BeZero())
   144  		Expect(sender.BandwidthEstimate()).To(Equal(infBandwidth))
   145  		// Make sure we can send.
   146  		Expect(sender.TimeUntilSend(0)).To(BeZero())
   147  
   148  		for i := 0; i < numberOfAcks; i++ {
   149  			// Send our full send window.
   150  			SendAvailableSendWindow()
   151  			AckNPackets(2)
   152  		}
   153  		cwnd := sender.GetCongestionWindow()
   154  		Expect(cwnd).To(Equal(defaultWindowTCP + maxDatagramSize*2*numberOfAcks))
   155  		Expect(sender.BandwidthEstimate()).To(Equal(BandwidthFromDelta(cwnd, rttStats.SmoothedRTT())))
   156  	})
   157  
   158  	It("slow start packet loss", func() {
   159  		const numberOfAcks = 10
   160  		for i := 0; i < numberOfAcks; i++ {
   161  			// Send our full send window.
   162  			SendAvailableSendWindow()
   163  			AckNPackets(2)
   164  		}
   165  		SendAvailableSendWindow()
   166  		expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks)
   167  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   168  
   169  		// Lose a packet to exit slow start.
   170  		LoseNPackets(1)
   171  		packetsInRecoveryWindow := expectedSendWindow / maxDatagramSize
   172  
   173  		// We should now have fallen out of slow start with a reduced window.
   174  		expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta)
   175  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   176  
   177  		// Recovery phase. We need to ack every packet in the recovery window before
   178  		// we exit recovery.
   179  		numberOfPacketsInWindow := expectedSendWindow / maxDatagramSize
   180  		AckNPackets(int(packetsInRecoveryWindow))
   181  		SendAvailableSendWindow()
   182  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   183  
   184  		// We need to ack an entire window before we increase CWND by 1.
   185  		AckNPackets(int(numberOfPacketsInWindow) - 2)
   186  		SendAvailableSendWindow()
   187  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   188  
   189  		// Next ack should increase cwnd by 1.
   190  		AckNPackets(1)
   191  		expectedSendWindow += maxDatagramSize
   192  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   193  
   194  		// Now RTO and ensure slow start gets reset.
   195  		Expect(sender.hybridSlowStart.Started()).To(BeTrue())
   196  		sender.OnRetransmissionTimeout(true)
   197  		Expect(sender.hybridSlowStart.Started()).To(BeFalse())
   198  	})
   199  
   200  	It("slow start packet loss PRR", func() {
   201  		// Test based on the first example in RFC6937.
   202  		// Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
   203  		const numberOfAcks = 5
   204  		for i := 0; i < numberOfAcks; i++ {
   205  			// Send our full send window.
   206  			SendAvailableSendWindow()
   207  			AckNPackets(2)
   208  		}
   209  		SendAvailableSendWindow()
   210  		expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks)
   211  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   212  
   213  		LoseNPackets(1)
   214  
   215  		// We should now have fallen out of slow start with a reduced window.
   216  		sendWindowBeforeLoss := expectedSendWindow
   217  		expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta)
   218  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   219  
   220  		// Testing TCP proportional rate reduction.
   221  		// We should send packets paced over the received acks for the remaining
   222  		// outstanding packets. The number of packets before we exit recovery is the
   223  		// original CWND minus the packet that has been lost and the one which
   224  		// triggered the loss.
   225  		remainingPacketsInRecovery := sendWindowBeforeLoss/maxDatagramSize - 2
   226  
   227  		for i := protocol.ByteCount(0); i < remainingPacketsInRecovery; i++ {
   228  			AckNPackets(1)
   229  			SendAvailableSendWindow()
   230  			Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   231  		}
   232  
   233  		// We need to ack another window before we increase CWND by 1.
   234  		numberOfPacketsInWindow := expectedSendWindow / maxDatagramSize
   235  		for i := protocol.ByteCount(0); i < numberOfPacketsInWindow; i++ {
   236  			AckNPackets(1)
   237  			Expect(SendAvailableSendWindow()).To(Equal(1))
   238  			Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   239  		}
   240  
   241  		AckNPackets(1)
   242  		expectedSendWindow += maxDatagramSize
   243  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   244  	})
   245  
   246  	It("slow start burst packet loss PRR", func() {
   247  		// Test based on the second example in RFC6937, though we also implement
   248  		// forward acknowledgements, so the first two incoming acks will trigger
   249  		// PRR immediately.
   250  		// Ack 20 packets in 10 acks to raise the CWND to 30.
   251  		const numberOfAcks = 10
   252  		for i := 0; i < numberOfAcks; i++ {
   253  			// Send our full send window.
   254  			SendAvailableSendWindow()
   255  			AckNPackets(2)
   256  		}
   257  		SendAvailableSendWindow()
   258  		expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks)
   259  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   260  
   261  		// Lose one more than the congestion window reduction, so that after loss,
   262  		// bytes_in_flight is lesser than the congestion window.
   263  		sendWindowAfterLoss := protocol.ByteCount(renoBeta * float32(expectedSendWindow))
   264  		numPacketsToLose := (expectedSendWindow-sendWindowAfterLoss)/maxDatagramSize + 1
   265  		LoseNPackets(int(numPacketsToLose))
   266  		// Immediately after the loss, ensure at least one packet can be sent.
   267  		// Losses without subsequent acks can occur with timer based loss detection.
   268  		Expect(sender.CanSend(bytesInFlight)).To(BeTrue())
   269  		AckNPackets(1)
   270  
   271  		// We should now have fallen out of slow start with a reduced window.
   272  		expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta)
   273  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   274  
   275  		// Only 2 packets should be allowed to be sent, per PRR-SSRB
   276  		Expect(SendAvailableSendWindow()).To(Equal(2))
   277  
   278  		// Ack the next packet, which triggers another loss.
   279  		LoseNPackets(1)
   280  		AckNPackets(1)
   281  
   282  		// Send 2 packets to simulate PRR-SSRB.
   283  		Expect(SendAvailableSendWindow()).To(Equal(2))
   284  
   285  		// Ack the next packet, which triggers another loss.
   286  		LoseNPackets(1)
   287  		AckNPackets(1)
   288  
   289  		// Send 2 packets to simulate PRR-SSRB.
   290  		Expect(SendAvailableSendWindow()).To(Equal(2))
   291  
   292  		// Exit recovery and return to sending at the new rate.
   293  		for i := 0; i < numberOfAcks; i++ {
   294  			AckNPackets(1)
   295  			Expect(SendAvailableSendWindow()).To(Equal(1))
   296  		}
   297  	})
   298  
   299  	It("RTO congestion window", func() {
   300  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   301  		Expect(sender.slowStartThreshold).To(Equal(protocol.MaxByteCount))
   302  
   303  		// Expect the window to decrease to the minimum once the RTO fires
   304  		// and slow start threshold to be set to 1/2 of the CWND.
   305  		sender.OnRetransmissionTimeout(true)
   306  		Expect(sender.GetCongestionWindow()).To(Equal(2 * maxDatagramSize))
   307  		Expect(sender.slowStartThreshold).To(Equal(5 * maxDatagramSize))
   308  	})
   309  
   310  	It("RTO congestion window no retransmission", func() {
   311  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   312  
   313  		// Expect the window to remain unchanged if the RTO fires but no
   314  		// packets are retransmitted.
   315  		sender.OnRetransmissionTimeout(false)
   316  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   317  	})
   318  
   319  	It("tcp cubic reset epoch on quiescence", func() {
   320  		const maxCongestionWindow = 50
   321  		const maxCongestionWindowBytes = maxCongestionWindow * maxDatagramSize
   322  		sender = newCubicSender(&clock, rttStats, false, protocol.InitialPacketSizeIPv4, initialCongestionWindowPackets*maxDatagramSize, maxCongestionWindowBytes, nil)
   323  
   324  		numSent := SendAvailableSendWindow()
   325  
   326  		// Make sure we fall out of slow start.
   327  		savedCwnd := sender.GetCongestionWindow()
   328  		LoseNPackets(1)
   329  		Expect(savedCwnd).To(BeNumerically(">", sender.GetCongestionWindow()))
   330  
   331  		// Ack the rest of the outstanding packets to get out of recovery.
   332  		for i := 1; i < numSent; i++ {
   333  			AckNPackets(1)
   334  		}
   335  		Expect(bytesInFlight).To(BeZero())
   336  
   337  		// Send a new window of data and ack all; cubic growth should occur.
   338  		savedCwnd = sender.GetCongestionWindow()
   339  		numSent = SendAvailableSendWindow()
   340  		for i := 0; i < numSent; i++ {
   341  			AckNPackets(1)
   342  		}
   343  		Expect(savedCwnd).To(BeNumerically("<", sender.GetCongestionWindow()))
   344  		Expect(maxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow()))
   345  		Expect(bytesInFlight).To(BeZero())
   346  
   347  		// Quiescent time of 100 seconds
   348  		clock.Advance(100 * time.Second)
   349  
   350  		// Send new window of data and ack one packet. Cubic epoch should have
   351  		// been reset; ensure cwnd increase is not dramatic.
   352  		savedCwnd = sender.GetCongestionWindow()
   353  		SendAvailableSendWindow()
   354  		AckNPackets(1)
   355  		Expect(savedCwnd).To(BeNumerically("~", sender.GetCongestionWindow(), maxDatagramSize))
   356  		Expect(maxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow()))
   357  	})
   358  
   359  	It("multiple losses in one window", func() {
   360  		SendAvailableSendWindow()
   361  		initialWindow := sender.GetCongestionWindow()
   362  		LosePacket(ackedPacketNumber + 1)
   363  		postLossWindow := sender.GetCongestionWindow()
   364  		Expect(initialWindow).To(BeNumerically(">", postLossWindow))
   365  		LosePacket(ackedPacketNumber + 3)
   366  		Expect(sender.GetCongestionWindow()).To(Equal(postLossWindow))
   367  		LosePacket(packetNumber - 1)
   368  		Expect(sender.GetCongestionWindow()).To(Equal(postLossWindow))
   369  
   370  		// Lose a later packet and ensure the window decreases.
   371  		LosePacket(packetNumber)
   372  		Expect(postLossWindow).To(BeNumerically(">", sender.GetCongestionWindow()))
   373  	})
   374  
   375  	It("1 connection congestion avoidance at end of recovery", func() {
   376  		// Ack 10 packets in 5 acks to raise the CWND to 20.
   377  		const numberOfAcks = 5
   378  		for i := 0; i < numberOfAcks; i++ {
   379  			// Send our full send window.
   380  			SendAvailableSendWindow()
   381  			AckNPackets(2)
   382  		}
   383  		SendAvailableSendWindow()
   384  		expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks)
   385  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   386  
   387  		LoseNPackets(1)
   388  
   389  		// We should now have fallen out of slow start with a reduced window.
   390  		expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta)
   391  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   392  
   393  		// No congestion window growth should occur in recovery phase, i.e., until the
   394  		// currently outstanding 20 packets are acked.
   395  		for i := 0; i < 10; i++ {
   396  			// Send our full send window.
   397  			SendAvailableSendWindow()
   398  			Expect(sender.InRecovery()).To(BeTrue())
   399  			AckNPackets(2)
   400  			Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   401  		}
   402  		Expect(sender.InRecovery()).To(BeFalse())
   403  
   404  		// Out of recovery now. Congestion window should not grow during RTT.
   405  		for i := protocol.ByteCount(0); i < expectedSendWindow/maxDatagramSize-2; i += 2 {
   406  			// Send our full send window.
   407  			SendAvailableSendWindow()
   408  			AckNPackets(2)
   409  			Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   410  		}
   411  
   412  		// Next ack should cause congestion window to grow by 1MSS.
   413  		SendAvailableSendWindow()
   414  		AckNPackets(2)
   415  		expectedSendWindow += maxDatagramSize
   416  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   417  	})
   418  
   419  	It("no PRR", func() {
   420  		SendAvailableSendWindow()
   421  		LoseNPackets(9)
   422  		AckNPackets(1)
   423  
   424  		Expect(sender.GetCongestionWindow()).To(Equal(protocol.ByteCount(renoBeta * float32(defaultWindowTCP))))
   425  		windowInPackets := renoBeta * float32(defaultWindowTCP) / float32(maxDatagramSize)
   426  		numSent := SendAvailableSendWindow()
   427  		Expect(numSent).To(BeEquivalentTo(windowInPackets))
   428  	})
   429  
   430  	It("reset after connection migration", func() {
   431  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   432  		Expect(sender.slowStartThreshold).To(Equal(protocol.MaxByteCount))
   433  
   434  		// Starts with slow start.
   435  		const numberOfAcks = 10
   436  		for i := 0; i < numberOfAcks; i++ {
   437  			// Send our full send window.
   438  			SendAvailableSendWindow()
   439  			AckNPackets(2)
   440  		}
   441  		SendAvailableSendWindow()
   442  		expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks)
   443  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   444  
   445  		// Loses a packet to exit slow start.
   446  		LoseNPackets(1)
   447  
   448  		// We should now have fallen out of slow start with a reduced window. Slow
   449  		// start threshold is also updated.
   450  		expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta)
   451  		Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow))
   452  		Expect(sender.slowStartThreshold).To(Equal(expectedSendWindow))
   453  
   454  		// Resets cwnd and slow start threshold on connection migrations.
   455  		sender.OnConnectionMigration()
   456  		Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
   457  		Expect(sender.slowStartThreshold).To(Equal(MaxCongestionWindow))
   458  		Expect(sender.hybridSlowStart.Started()).To(BeFalse())
   459  	})
   460  
   461  	It("slow starts up to the maximum congestion window", func() {
   462  		const initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize
   463  		sender = newCubicSender(&clock, rttStats, true, protocol.InitialPacketSizeIPv4, initialCongestionWindowPackets*maxDatagramSize, initialMaxCongestionWindow, nil)
   464  
   465  		for i := 1; i < protocol.MaxCongestionWindowPackets; i++ {
   466  			sender.MaybeExitSlowStart()
   467  			sender.OnPacketAcked(protocol.PacketNumber(i), 1350, sender.GetCongestionWindow(), clock.Now())
   468  		}
   469  		Expect(sender.GetCongestionWindow()).To(Equal(initialMaxCongestionWindow))
   470  	})
   471  
   472  	It("doesn't allow reductions of the maximum packet size", func() {
   473  		Expect(func() { sender.SetMaxDatagramSize(initialMaxDatagramSize - 1) }).To(Panic())
   474  	})
   475  
   476  	It("slow starts up to maximum congestion window, if larger packets are sent", func() {
   477  		const initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize
   478  		sender = newCubicSender(&clock, rttStats, true, protocol.InitialPacketSizeIPv4, initialCongestionWindowPackets*maxDatagramSize, initialMaxCongestionWindow, nil)
   479  		const packetSize = initialMaxDatagramSize + 100
   480  		sender.SetMaxDatagramSize(packetSize)
   481  		for i := 1; i < protocol.MaxCongestionWindowPackets; i++ {
   482  			sender.OnPacketAcked(protocol.PacketNumber(i), packetSize, sender.GetCongestionWindow(), clock.Now())
   483  		}
   484  		const maxCwnd = protocol.MaxCongestionWindowPackets * packetSize
   485  		Expect(sender.GetCongestionWindow()).To(And(
   486  			BeNumerically(">", maxCwnd),
   487  			BeNumerically("<=", maxCwnd+packetSize),
   488  		))
   489  	})
   490  
   491  	It("limit cwnd increase in congestion avoidance", func() {
   492  		// Enable Cubic.
   493  		sender = newCubicSender(&clock, rttStats, false, protocol.InitialPacketSizeIPv4, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow, nil)
   494  		numSent := SendAvailableSendWindow()
   495  
   496  		// Make sure we fall out of slow start.
   497  		savedCwnd := sender.GetCongestionWindow()
   498  		LoseNPackets(1)
   499  		Expect(savedCwnd).To(BeNumerically(">", sender.GetCongestionWindow()))
   500  
   501  		// Ack the rest of the outstanding packets to get out of recovery.
   502  		for i := 1; i < numSent; i++ {
   503  			AckNPackets(1)
   504  		}
   505  		Expect(bytesInFlight).To(BeZero())
   506  
   507  		savedCwnd = sender.GetCongestionWindow()
   508  		SendAvailableSendWindow()
   509  
   510  		// Ack packets until the CWND increases.
   511  		for sender.GetCongestionWindow() == savedCwnd {
   512  			AckNPackets(1)
   513  			SendAvailableSendWindow()
   514  		}
   515  		// Bytes in flight may be larger than the CWND if the CWND isn't an exact
   516  		// multiple of the packet sizes being sent.
   517  		Expect(bytesInFlight).To(BeNumerically(">=", sender.GetCongestionWindow()))
   518  		savedCwnd = sender.GetCongestionWindow()
   519  
   520  		// Advance time 2 seconds waiting for an ack.
   521  		clock.Advance(2 * time.Second)
   522  
   523  		// Ack two packets.  The CWND should increase by only one packet.
   524  		AckNPackets(2)
   525  		Expect(sender.GetCongestionWindow()).To(Equal(savedCwnd + maxDatagramSize))
   526  	})
   527  })