blob: 4c589a732f81fc153152d97411cb01619d401515 [file] [log] [blame]
#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