Moves //gfe/gfe2/http2:window_manager to //third_party/http2/adapter.
PiperOrigin-RevId: 364328994
Change-Id: I0899a629122cfe88f2f952ad5dae00e1331c57bc
diff --git a/http2/adapter/window_manager_test.cc b/http2/adapter/window_manager_test.cc
new file mode 100644
index 0000000..4c589a7
--- /dev/null
+++ b/http2/adapter/window_manager_test.cc
@@ -0,0 +1,172 @@
+#include "http2/adapter/window_manager.h"
+
+#include <list>
+
+#include "testing/base/public/gmock.h"
+#include "testing/base/public/gunit.h"
+#include "absl/functional/bind_front.h"
+#include "spdy/platform/api/spdy_test_helpers.h"
+#include "util/random/acmrandom.h"
+
+using ::absl::bind_front;
+
+namespace http2 {
+namespace adapter {
+
+// Use the peer to access private vars of WindowManager.
+class WindowManagerPeer {
+ public:
+ explicit WindowManagerPeer(const WindowManager& wm) : wm_(wm) {}
+
+ size_t buffered() {
+ return wm_.buffered_;
+ }
+
+ private:
+ const WindowManager& wm_;
+};
+
+class WindowManagerTest : public ::testing::Test {
+ protected:
+ WindowManagerTest()
+ : wm_(kDefaultLimit, bind_front(&WindowManagerTest::OnCall, this)),
+ peer_(wm_),
+ random_(ACMRandom::HostnamePidTimeSeed()) {}
+
+ void OnCall(size_t s) {
+ call_sequence_.push_back(s);
+ }
+
+ const size_t kDefaultLimit = 32 * 1024 * 3;
+ std::list<size_t> call_sequence_;
+ WindowManager wm_;
+ WindowManagerPeer peer_;
+ ACMRandom random_;
+};
+
+// A few no-op calls.
+TEST_F(WindowManagerTest, NoOps) {
+ wm_.SetWindowSizeLimit(kDefaultLimit);
+ wm_.SetWindowSizeLimit(0);
+ wm_.SetWindowSizeLimit(kDefaultLimit);
+ wm_.MarkDataBuffered(0);
+ wm_.MarkDataFlushed(0);
+ EXPECT_TRUE(call_sequence_.empty());
+}
+
+// This test verifies that WindowManager does not notify its listener when data
+// is only buffered, and never flushed.
+TEST_F(WindowManagerTest, DataOnlyBuffered) {
+ size_t total = 0;
+ while (total < kDefaultLimit) {
+ size_t s = std::min<size_t>(kDefaultLimit - total,
+ random_.UnbiasedUniform64(1024));
+ total += s;
+ wm_.MarkDataBuffered(s);
+ }
+ EXPECT_THAT(call_sequence_, ::testing::IsEmpty());
+}
+
+// This test verifies that WindowManager does notify its listener when data is
+// buffered and subsequently flushed.
+TEST_F(WindowManagerTest, DataBufferedAndFlushed) {
+ size_t total_buffered = 0;
+ size_t total_flushed = 0;
+ while (call_sequence_.empty()) {
+ size_t buffered = std::min<size_t>(kDefaultLimit - total_buffered,
+ random_.UnbiasedUniform64(1024));
+ wm_.MarkDataBuffered(buffered);
+ total_buffered += buffered;
+ EXPECT_TRUE(call_sequence_.empty());
+ size_t flushed =
+ random_.UnbiasedUniform64(total_buffered - total_flushed);
+ wm_.MarkDataFlushed(flushed);
+ total_flushed += flushed;
+ }
+ // If WindowManager decided to send an update, at least one third of the
+ // window must have been consumed by buffered data.
+ EXPECT_GE(total_buffered, kDefaultLimit / 3);
+}
+
+// Window manager should avoid window underflow.
+TEST_F(WindowManagerTest, AvoidWindowUnderflow) {
+ EXPECT_EQ(wm_.CurrentWindowSize(), wm_.WindowSizeLimit());
+ // Don't buffer more than the total window!
+ wm_.MarkDataBuffered(wm_.WindowSizeLimit() + 1);
+ EXPECT_EQ(wm_.CurrentWindowSize(), 0);
+}
+
+// Window manager should GFE_BUG and avoid buffered underflow.
+TEST_F(WindowManagerTest, AvoidBufferedUnderflow) {
+ EXPECT_EQ(peer_.buffered(), 0);
+ // Don't flush more than has been buffered!
+ EXPECT_SPDY_BUG(wm_.MarkDataFlushed(1), "buffered underflow");
+ EXPECT_EQ(peer_.buffered(), 0);
+
+ wm_.MarkDataBuffered(42);
+ EXPECT_EQ(peer_.buffered(), 42);
+ // Don't flush more than has been buffered!
+ EXPECT_SPDY_BUG(wm_.MarkDataFlushed(43), "buffered underflow");
+ EXPECT_EQ(peer_.buffered(), 0);
+}
+
+// This test verifies that WindowManager notifies its listener when window is
+// consumed (data is ignored or immediately dropped).
+TEST_F(WindowManagerTest, WindowConsumed) {
+ size_t consumed = kDefaultLimit / 3 - 1;
+ wm_.MarkWindowConsumed(consumed);
+ EXPECT_TRUE(call_sequence_.empty());
+ const size_t extra = 1;
+ wm_.MarkWindowConsumed(extra);
+ EXPECT_THAT(call_sequence_, testing::ElementsAre(consumed + extra));
+}
+
+// This test verifies that WindowManager notifies its listener when the window
+// size limit is increased.
+TEST_F(WindowManagerTest, ListenerCalledOnSizeUpdate) {
+ wm_.SetWindowSizeLimit(kDefaultLimit - 1024);
+ EXPECT_TRUE(call_sequence_.empty());
+ wm_.SetWindowSizeLimit(kDefaultLimit * 5);
+ // Because max(outstanding window, previous limit) is kDefaultLimit, it is
+ // only appropriate to increase the window by kDefaultLimit * 4.
+ EXPECT_THAT(call_sequence_, testing::ElementsAre(kDefaultLimit * 4));
+}
+
+// This test verifies that when data is buffered and then the limit is
+// decreased, WindowManager only notifies the listener once any outstanding
+// window has been consumed.
+TEST_F(WindowManagerTest, WindowUpdateAfterLimitDecreased) {
+ wm_.MarkDataBuffered(kDefaultLimit - 1024);
+ wm_.SetWindowSizeLimit(kDefaultLimit - 2048);
+
+ // Now there are 2048 bytes of window outstanding beyond the current limit,
+ // and we have 1024 bytes of data buffered beyond the current limit. This is
+ // intentional, to be sure that WindowManager works properly if the limit is
+ // decreased at runtime.
+
+ wm_.MarkDataFlushed(512);
+ EXPECT_TRUE(call_sequence_.empty());
+ wm_.MarkDataFlushed(512);
+ EXPECT_TRUE(call_sequence_.empty());
+ wm_.MarkDataFlushed(512);
+ EXPECT_TRUE(call_sequence_.empty());
+ wm_.MarkDataFlushed(1024);
+ EXPECT_THAT(call_sequence_, testing::ElementsAre(512));
+}
+
+// For normal behavior, we only call MaybeNotifyListener() when data is
+// flushed. But if window runs out entirely, we still need to call
+// MaybeNotifyListener() to avoid becoming artificially blocked when data isn't
+// being flushed.
+TEST_F(WindowManagerTest, ZeroWindowNotification) {
+ // Consume a byte of window, but not enough to trigger an update.
+ wm_.MarkWindowConsumed(1);
+
+ // Buffer the remaining window.
+ wm_.MarkDataBuffered(kDefaultLimit - 1);
+ // Listener is notified of the remaining byte of possible window.
+ EXPECT_THAT(call_sequence_, testing::ElementsAre(1));
+}
+
+} // namespace adapter
+} // namespace http2