blob: 2371f9f3f19312b9312ca971285bcea2fd7cdaf4 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/frame_token_message_queue.h"
#include <vector>
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
// Test verision of FrameTokenMessageQueue::Client which tracks the number of
// calls to client methods, and the associated input parameters.
class TestFrameTokenMessageQueueClient : public FrameTokenMessageQueue::Client {
public:
TestFrameTokenMessageQueueClient() {}
TestFrameTokenMessageQueueClient(const TestFrameTokenMessageQueueClient&) =
delete;
TestFrameTokenMessageQueueClient& operator=(
const TestFrameTokenMessageQueueClient&) = delete;
~TestFrameTokenMessageQueueClient() {}
// Resets all method counters.
void Reset();
// FrameTokenMessageQueue::Client:
void OnInvalidFrameToken(uint32_t frame_token) override;
bool invalid_frame_token_called() const {
return invalid_frame_token_called_;
}
uint32_t invalid_frame_token() const { return invalid_frame_token_; }
private:
bool invalid_frame_token_called_ = false;
uint32_t invalid_frame_token_ = 0u;
};
void TestFrameTokenMessageQueueClient::Reset() {
invalid_frame_token_called_ = false;
invalid_frame_token_ = 0u;
}
void TestFrameTokenMessageQueueClient::OnInvalidFrameToken(
uint32_t frame_token) {
invalid_frame_token_called_ = true;
invalid_frame_token_ = frame_token;
}
// Test class which provides FrameTokenCallback() to be used as a closure when
// enqueueing non-IPC callbacks. This only tracks if the callback was called.
class TestNonIPCMessageEnqueuer {
public:
TestNonIPCMessageEnqueuer() {}
TestNonIPCMessageEnqueuer(const TestNonIPCMessageEnqueuer&) = delete;
TestNonIPCMessageEnqueuer& operator=(const TestNonIPCMessageEnqueuer&) =
delete;
~TestNonIPCMessageEnqueuer() {}
void FrameTokenCallback(base::TimeTicks activation_time);
bool frame_token_callback_called() const {
return frame_token_callback_called_;
}
private:
bool frame_token_callback_called_ = false;
};
void TestNonIPCMessageEnqueuer::FrameTokenCallback(
base::TimeTicks activation_time) {
frame_token_callback_called_ = true;
}
} // namespace
class FrameTokenMessageQueueTest : public testing::Test {
public:
FrameTokenMessageQueueTest();
FrameTokenMessageQueueTest(const FrameTokenMessageQueueTest&) = delete;
FrameTokenMessageQueueTest& operator=(const FrameTokenMessageQueueTest&) =
delete;
~FrameTokenMessageQueueTest() override {}
TestFrameTokenMessageQueueClient* test_client() { return &test_client_; }
TestNonIPCMessageEnqueuer* test_non_ipc_enqueuer() {
return &test_non_ipc_enqueuer_;
}
FrameTokenMessageQueue* frame_token_message_queue() {
return &frame_token_message_queue_;
}
private:
TestFrameTokenMessageQueueClient test_client_;
TestNonIPCMessageEnqueuer test_non_ipc_enqueuer_;
FrameTokenMessageQueue frame_token_message_queue_;
};
FrameTokenMessageQueueTest::FrameTokenMessageQueueTest() {
frame_token_message_queue_.Init(&test_client_);
}
// Tests that if we only have a non-IPC callback enqueued that it is called once
// the frame token arrive.
TEST_F(FrameTokenMessageQueueTest, EnqueueOnlyNonIPC) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
const uint32_t frame_token = 42;
queue->EnqueueOrRunFrameTokenCallback(
frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_EQ(1u, queue->size());
EXPECT_FALSE(enqueuer->frame_token_callback_called());
EXPECT_FALSE(client->invalid_frame_token_called());
queue->DidProcessFrame(frame_token, base::TimeTicks::Now());
EXPECT_EQ(0u, queue->size());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_TRUE(enqueuer->frame_token_callback_called());
}
// Verifies that if there are multiple non-IPC messages enqueued that they are
// all called.
TEST_F(FrameTokenMessageQueueTest, MultipleNonIPCMessages) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
const uint32_t frame_token = 42;
queue->EnqueueOrRunFrameTokenCallback(
frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_FALSE(enqueuer->frame_token_callback_called());
EXPECT_EQ(1u, queue->size());
// Create a second callback
TestNonIPCMessageEnqueuer second_enqueuer;
queue->EnqueueOrRunFrameTokenCallback(
frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(&second_enqueuer)));
EXPECT_FALSE(second_enqueuer.frame_token_callback_called());
EXPECT_EQ(2u, queue->size());
queue->DidProcessFrame(frame_token, base::TimeTicks::Now());
EXPECT_EQ(0u, queue->size());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_TRUE(enqueuer->frame_token_callback_called());
EXPECT_TRUE(second_enqueuer.frame_token_callback_called());
}
// Tests that if a non-IPC callback is enqueued, after its frame token as been
// received, that it is immediately processed.
TEST_F(FrameTokenMessageQueueTest, EnqueuedAfterFrameTokenImmediatelyRuns) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
const uint32_t frame_token = 42;
queue->DidProcessFrame(frame_token, base::TimeTicks::Now());
EXPECT_EQ(0u, queue->size());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_FALSE(enqueuer->frame_token_callback_called());
queue->EnqueueOrRunFrameTokenCallback(
frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_EQ(0u, queue->size());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_TRUE(enqueuer->frame_token_callback_called());
}
// Test that if non-IPC callbacks are enqueued for different frame tokens, that
// we only process the messages associated with the arriving token, and keep the
// others enqueued.
TEST_F(FrameTokenMessageQueueTest, DifferentFrameTokensEnqueuedNonIPC) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
const uint32_t frame_token_1 = 42;
queue->EnqueueOrRunFrameTokenCallback(
frame_token_1,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_FALSE(enqueuer->frame_token_callback_called());
EXPECT_EQ(1u, queue->size());
// Create a second callback
const uint32_t frame_token_2 = 1337;
TestNonIPCMessageEnqueuer second_enqueuer;
queue->EnqueueOrRunFrameTokenCallback(
frame_token_2,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(&second_enqueuer)));
EXPECT_FALSE(second_enqueuer.frame_token_callback_called());
EXPECT_EQ(2u, queue->size());
queue->DidProcessFrame(frame_token_1, base::TimeTicks::Now());
EXPECT_EQ(1u, queue->size());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_TRUE(enqueuer->frame_token_callback_called());
EXPECT_FALSE(second_enqueuer.frame_token_callback_called());
queue->DidProcessFrame(frame_token_2, base::TimeTicks::Now());
EXPECT_TRUE(second_enqueuer.frame_token_callback_called());
}
// Tests that if DidProcessFrame is called with an invalid token, that it is
// rejected, and that no callbacks are processed.
TEST_F(FrameTokenMessageQueueTest, InvalidDidProcessFrameTokenNotProcessed) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
const uint32_t frame_token = 42;
queue->EnqueueOrRunFrameTokenCallback(
frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_FALSE(enqueuer->frame_token_callback_called());
EXPECT_FALSE(client->invalid_frame_token_called());
// Empty token should be invalid even with no process frames processed.
const uint32_t invalid_frame_token = 0;
queue->DidProcessFrame(invalid_frame_token, base::TimeTicks::Now());
EXPECT_EQ(1u, queue->size());
EXPECT_TRUE(client->invalid_frame_token_called());
EXPECT_EQ(invalid_frame_token, client->invalid_frame_token());
EXPECT_FALSE(enqueuer->frame_token_callback_called());
}
// Test that if DidProcessFrame is called with an earlier frame token, that it
// is rejected, and that no callbacks are processed.
TEST_F(FrameTokenMessageQueueTest, EarlierTokenForDidProcessFrameRejected) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
// Settings a low value frame token will not block enqueueing.
const uint32_t earlier_frame_token = 42;
queue->DidProcessFrame(earlier_frame_token, base::TimeTicks::Now());
const uint32_t frame_token = 1337;
queue->EnqueueOrRunFrameTokenCallback(
frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_FALSE(enqueuer->frame_token_callback_called());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_EQ(1u, queue->size());
// Using a frame token that is earlier than the last received should be
// rejected.
const uint32_t invalid_frame_token = earlier_frame_token - 1;
queue->DidProcessFrame(invalid_frame_token, base::TimeTicks::Now());
EXPECT_EQ(1u, queue->size());
EXPECT_TRUE(client->invalid_frame_token_called());
EXPECT_EQ(invalid_frame_token, client->invalid_frame_token());
EXPECT_FALSE(enqueuer->frame_token_callback_called());
}
// Tests that if we have already enqueued a callback for a frame token, that if
// a request for an earlier frame token arrives, that it is still enqueued. Then
// once the large frame token arrives, both are processed.
TEST_F(FrameTokenMessageQueueTest, OutOfOrderFrameTokensEnqueue) {
FrameTokenMessageQueue* queue = frame_token_message_queue();
TestFrameTokenMessageQueueClient* client = test_client();
TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
ASSERT_EQ(0u, queue->size());
const uint32_t larger_frame_token = 1337;
queue->EnqueueOrRunFrameTokenCallback(
larger_frame_token,
base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback,
base::Unretained(enqueuer)));
EXPECT_EQ(1u, queue->size());
EXPECT_FALSE(enqueuer->frame_token_callback_called());
EXPECT_FALSE(client->invalid_frame_token_called());
// Process both with the larger frame token arriving.
queue->DidProcessFrame(larger_frame_token, base::TimeTicks::Now());
EXPECT_EQ(0u, queue->size());
EXPECT_FALSE(client->invalid_frame_token_called());
EXPECT_TRUE(enqueuer->frame_token_callback_called());
}
} // namespace content