| #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 |