github.com/tumi8/quic-go@v0.37.4-tum/noninternal/congestion/cubic_sender_test.go (about)

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