blob: f0677310a5760922803d3b615fe33ff9f9a47eab [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/websockets/websocket_message_chunk_accumulator.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
namespace blink {
namespace {
class WebSocketMessageChunkAccumulatorTest : public testing::Test {
public:
using FakeTaskRunner = scheduler::FakeTaskRunner;
static Vector<char> Flatten(const Vector<base::span<const char>>& chunks) {
Vector<char> v;
for (const auto& chunk : chunks) {
v.Append(chunk.data(), chunk.size());
}
return v;
}
static constexpr auto kSegmentSize =
WebSocketMessageChunkAccumulator::kSegmentSize;
static constexpr auto kFreeDelay =
WebSocketMessageChunkAccumulator::kFreeDelay;
};
constexpr size_t WebSocketMessageChunkAccumulatorTest::kSegmentSize;
constexpr base::TimeDelta WebSocketMessageChunkAccumulatorTest::kFreeDelay;
TEST_F(WebSocketMessageChunkAccumulatorTest, Empty) {
WebSocketMessageChunkAccumulator chunks(
base::MakeRefCounted<FakeTaskRunner>());
EXPECT_EQ(chunks.GetSize(), 0u);
EXPECT_TRUE(chunks.GetView().IsEmpty());
}
TEST_F(WebSocketMessageChunkAccumulatorTest, Append) {
WebSocketMessageChunkAccumulator chunks(
base::MakeRefCounted<FakeTaskRunner>());
Vector<char> chunk(8, 'x');
chunks.Append(base::make_span(chunk));
EXPECT_EQ(chunks.GetSize(), chunk.size());
EXPECT_EQ(8u, chunks.GetSize());
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(chunks.GetView()[0].size(), 8u);
ASSERT_EQ(Flatten(chunks.GetView()), chunk);
}
TEST_F(WebSocketMessageChunkAccumulatorTest, AppendChunkWithInternalChunkSize) {
WebSocketMessageChunkAccumulator chunks(
base::MakeRefCounted<FakeTaskRunner>());
Vector<char> chunk(kSegmentSize, 'y');
chunks.Append(base::make_span(chunk));
EXPECT_EQ(chunks.GetSize(), chunk.size());
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(chunks.GetView()[0].size(), kSegmentSize);
ASSERT_EQ(Flatten(chunks.GetView()), chunk);
}
TEST_F(WebSocketMessageChunkAccumulatorTest, AppendLargeChunk) {
WebSocketMessageChunkAccumulator chunks(
base::MakeRefCounted<FakeTaskRunner>());
Vector<char> chunk(kSegmentSize * 2 + 2, 'y');
chunks.Append(base::make_span(chunk));
EXPECT_EQ(chunks.GetSize(), chunk.size());
ASSERT_EQ(chunks.GetView().size(), 3u);
ASSERT_EQ(chunks.GetView()[0].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[1].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[2].size(), 2u);
ASSERT_EQ(Flatten(chunks.GetView()), chunk);
}
TEST_F(WebSocketMessageChunkAccumulatorTest, AppendRepeatedly) {
WebSocketMessageChunkAccumulator chunks(
base::MakeRefCounted<FakeTaskRunner>());
Vector<char> chunk1(8, 'a');
Vector<char> chunk2(4, 'b');
Vector<char> chunk3; // empty
Vector<char> chunk4(kSegmentSize * 3 - 12, 'd');
Vector<char> chunk5(6, 'e');
Vector<char> chunk6(kSegmentSize - 5, 'f');
// This will grow over time.
Vector<char> expected;
chunks.Append(base::make_span(chunk1));
expected.AppendVector(chunk1);
EXPECT_EQ(chunks.GetSize(), expected.size());
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(chunks.GetView()[0].size(), 8u);
ASSERT_EQ(Flatten(chunks.GetView()), expected);
chunks.Append(base::make_span(chunk2));
expected.AppendVector(chunk2);
EXPECT_EQ(chunks.GetSize(), expected.size());
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(chunks.GetView()[0].size(), 12u);
ASSERT_EQ(Flatten(chunks.GetView()), expected);
chunks.Append(base::make_span(chunk3));
expected.AppendVector(chunk3);
EXPECT_EQ(chunks.GetSize(), expected.size());
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(chunks.GetView()[0].size(), 12u);
ASSERT_EQ(Flatten(chunks.GetView()), expected);
chunks.Append(base::make_span(chunk4));
expected.AppendVector(chunk4);
EXPECT_EQ(chunks.GetSize(), expected.size());
ASSERT_EQ(chunks.GetView().size(), 3u);
ASSERT_EQ(chunks.GetView()[0].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[1].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[2].size(), kSegmentSize);
ASSERT_EQ(Flatten(chunks.GetView()), expected);
chunks.Append(base::make_span(chunk5));
expected.AppendVector(chunk5);
EXPECT_EQ(chunks.GetSize(), expected.size());
ASSERT_EQ(chunks.GetView().size(), 4u);
ASSERT_EQ(chunks.GetView()[0].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[1].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[2].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[3].size(), 6u);
ASSERT_EQ(Flatten(chunks.GetView()), expected);
chunks.Append(base::make_span(chunk6));
expected.AppendVector(chunk6);
EXPECT_EQ(chunks.GetSize(), expected.size());
ASSERT_EQ(chunks.GetView().size(), 5u);
ASSERT_EQ(chunks.GetView()[0].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[1].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[2].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[3].size(), kSegmentSize);
ASSERT_EQ(chunks.GetView()[4].size(), 1u);
ASSERT_EQ(Flatten(chunks.GetView()), expected);
}
TEST_F(WebSocketMessageChunkAccumulatorTest, ClearAndAppend) {
WebSocketMessageChunkAccumulator chunks(
base::MakeRefCounted<FakeTaskRunner>());
Vector<char> chunk1(8, 'x');
Vector<char> chunk2(3, 'y');
chunks.Clear();
EXPECT_EQ(chunks.GetSize(), 0u);
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
chunks.Append(base::make_span(chunk1));
EXPECT_EQ(chunks.GetSize(), 8u);
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(Flatten(chunks.GetView()), chunk1);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
chunks.Clear();
EXPECT_EQ(chunks.GetSize(), 0u);
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 1u);
chunks.Append(base::make_span(chunk2));
EXPECT_EQ(chunks.GetSize(), 3u);
ASSERT_EQ(chunks.GetView().size(), 1u);
ASSERT_EQ(Flatten(chunks.GetView()), chunk2);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
}
TEST_F(WebSocketMessageChunkAccumulatorTest, ClearTimer) {
auto task_runner = base::MakeRefCounted<FakeTaskRunner>();
WebSocketMessageChunkAccumulator chunks(task_runner);
Vector<char> chunk1(kSegmentSize * 4, 'x');
Vector<char> chunk2(kSegmentSize * 3, 'x');
Vector<char> chunk3(kSegmentSize * 1, 'x');
// We don't start the timer because GetPoolSizeForTesting() is 0.
chunks.Clear();
EXPECT_FALSE(chunks.IsTimerActiveForTesting());
EXPECT_EQ(chunks.GetSize(), 0u);
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
chunks.Append(base::make_span(chunk1));
ASSERT_EQ(chunks.GetView().size(), 4u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
// We start the timer here.
// |num_pooled_segments_to_be_removed_| is 4.
chunks.Clear();
EXPECT_TRUE(chunks.IsTimerActiveForTesting());
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 4u);
chunks.Append(base::make_span(chunk2));
ASSERT_EQ(chunks.GetView().size(), 3u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 1u);
// We don't start the timer because it's already active.
// |num_pooled_segments_to_be_removed_| is set to 1.
chunks.Clear();
EXPECT_TRUE(chunks.IsTimerActiveForTesting());
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 4u);
// We remove 1 chunk from |pooled_segments_|.
// We start the timer because |num_pooled_segments_to_be_removed_| > 0.
task_runner->AdvanceTimeAndRun(kFreeDelay);
EXPECT_TRUE(chunks.IsTimerActiveForTesting());
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 3u);
chunks.Append(base::make_span(chunk3));
ASSERT_EQ(chunks.GetView().size(), 1u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 2u);
// We remove 2 chunks from |pooled_segments_|.
// |num_pooled_segments_to_be_removed_| is 3 but we only have 2 pooled
// segments. We don't start the timer because we don't have pooled
// segments any more.
task_runner->AdvanceTimeAndRun(kFreeDelay);
EXPECT_FALSE(chunks.IsTimerActiveForTesting());
ASSERT_EQ(chunks.GetView().size(), 1u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
// We start the timer here. num_pooled_segments_to_be_removed_ is set to 1.
chunks.Clear();
EXPECT_TRUE(chunks.IsTimerActiveForTesting());
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 1u);
// We remove 1 chunk from |pooled_segments_|.
// We don't start the timer because we don't have pooled segments any more.
task_runner->AdvanceTimeAndRun(kFreeDelay);
EXPECT_FALSE(chunks.IsTimerActiveForTesting());
ASSERT_EQ(chunks.GetView().size(), 0u);
EXPECT_EQ(chunks.GetPoolSizeForTesting(), 0u);
}
} // namespace
} // namespace blink