blob: 3ae09670e401415c2017fc63593aa1a66d7fd3a3 [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 "components/performance_manager/graph/worker_node_impl.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace performance_manager {
namespace {
class WorkerNodeImplTest : public GraphTestHarness {
public:
protected:
};
} // namespace
TEST_F(WorkerNodeImplTest, SafeDowncast) {
auto process = CreateNode<ProcessNodeImpl>();
auto worker = CreateNode<WorkerNodeImpl>(WorkerNode::WorkerType::kDedicated,
process.get());
WorkerNode* node = worker.get();
EXPECT_EQ(worker.get(), WorkerNodeImpl::FromNode(node));
NodeBase* base = worker.get();
EXPECT_EQ(base, NodeBase::FromNode(node));
EXPECT_EQ(static_cast<Node*>(node), base->ToNode());
}
using WorkerNodeImplDeathTest = WorkerNodeImplTest;
TEST_F(WorkerNodeImplDeathTest, SafeDowncast) {
auto process = CreateNode<ProcessNodeImpl>();
auto worker = CreateNode<WorkerNodeImpl>(WorkerNode::WorkerType::kDedicated,
process.get());
ASSERT_DEATH_IF_SUPPORTED(FrameNodeImpl::FromNodeBase(worker.get()), "");
}
TEST_F(WorkerNodeImplTest, ConstProperties) {
const WorkerNode::WorkerType kWorkerType = WorkerNode::WorkerType::kShared;
const std::string kTestBrowserContextId =
base::UnguessableToken::Create().ToString();
auto process = CreateNode<ProcessNodeImpl>();
static const GURL kTestUrl("testurl.com");
static const base::UnguessableToken kTestDevToolsToken =
base::UnguessableToken::Create();
auto worker_impl = CreateNode<WorkerNodeImpl>(kWorkerType, process.get(),
kTestBrowserContextId, kTestUrl,
kTestDevToolsToken);
// Test private interface.
EXPECT_EQ(worker_impl->browser_context_id(), kTestBrowserContextId);
EXPECT_EQ(worker_impl->worker_type(), kWorkerType);
EXPECT_EQ(worker_impl->process_node(), process.get());
EXPECT_EQ(worker_impl->url(), kTestUrl);
EXPECT_EQ(worker_impl->dev_tools_token(), kTestDevToolsToken);
// Test public interface.
const WorkerNode* worker = worker_impl.get();
EXPECT_EQ(worker->GetBrowserContextID(), kTestBrowserContextId);
EXPECT_EQ(worker->GetWorkerType(), kWorkerType);
EXPECT_EQ(worker->GetProcessNode(), process.get());
EXPECT_EQ(worker->GetURL(), kTestUrl);
EXPECT_EQ(worker->GetDevToolsToken(), kTestDevToolsToken);
}
// Create a worker of each type and register the frame as a client of each.
TEST_F(WorkerNodeImplTest, AddWorkerNodes) {
auto process = CreateNode<ProcessNodeImpl>();
auto page = CreateNode<PageNodeImpl>();
auto frame = CreateFrameNodeAutoId(process.get(), page.get());
auto dedicated_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto shared_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kShared, process.get());
auto service_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kService, process.get());
// Each workers have no clients.
EXPECT_TRUE(dedicated_worker->client_frames().empty());
EXPECT_TRUE(shared_worker->client_frames().empty());
EXPECT_TRUE(service_worker->client_frames().empty());
// The client frame doesn't have any child worker yet.
EXPECT_TRUE(frame->child_worker_nodes().empty());
dedicated_worker->AddClientFrame(frame.get());
shared_worker->AddClientFrame(frame.get());
service_worker->AddClientFrame(frame.get());
// Each workers have one client frame.
EXPECT_EQ(dedicated_worker->client_frames().size(), 1u);
EXPECT_EQ(shared_worker->client_frames().size(), 1u);
EXPECT_EQ(service_worker->client_frames().size(), 1u);
// The client frame knows about the 3 workers.
EXPECT_EQ(frame->child_worker_nodes().size(), 3u);
// Remove client connections.
service_worker->RemoveClientFrame(frame.get());
shared_worker->RemoveClientFrame(frame.get());
dedicated_worker->RemoveClientFrame(frame.get());
}
// Create a frame and a worker of each type that are all clients of the service
// worker.
TEST_F(WorkerNodeImplTest, ClientsOfServiceWorkers) {
auto process = CreateNode<ProcessNodeImpl>();
auto page = CreateNode<PageNodeImpl>();
auto frame = CreateFrameNodeAutoId(process.get(), page.get());
auto dedicated_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto shared_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kShared, process.get());
auto service_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kService, process.get());
// The service worker has no clients.
EXPECT_TRUE(service_worker->client_frames().empty());
EXPECT_TRUE(service_worker->client_workers().empty());
// The frame and the other workers aren't connected to the service worker yet.
EXPECT_TRUE(frame->child_worker_nodes().empty());
EXPECT_TRUE(dedicated_worker->child_workers().empty());
EXPECT_TRUE(shared_worker->child_workers().empty());
service_worker->AddClientFrame(frame.get());
service_worker->AddClientWorker(dedicated_worker.get());
service_worker->AddClientWorker(shared_worker.get());
EXPECT_EQ(service_worker->client_frames().size(), 1u);
EXPECT_EQ(service_worker->client_workers().size(), 2u);
EXPECT_EQ(frame->child_worker_nodes().size(), 1u);
EXPECT_EQ(shared_worker->child_workers().size(), 1u);
EXPECT_EQ(dedicated_worker->child_workers().size(), 1u);
// Remove client connections.
service_worker->RemoveClientWorker(shared_worker.get());
service_worker->RemoveClientWorker(dedicated_worker.get());
service_worker->RemoveClientFrame(frame.get());
}
// Create a hierarchy of nested dedicated workers where the parent one has 2
// children and one grandchildren.
TEST_F(WorkerNodeImplTest, NestedDedicatedWorkers) {
auto process = CreateNode<ProcessNodeImpl>();
auto page = CreateNode<PageNodeImpl>();
auto frame = CreateFrameNodeAutoId(process.get(), page.get());
auto parent_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto child_worker_1 = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto child_worker_2 = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto grandchild_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
parent_worker->AddClientFrame(frame.get());
child_worker_1->AddClientWorker(parent_worker.get());
child_worker_2->AddClientWorker(parent_worker.get());
grandchild_worker->AddClientWorker(child_worker_1.get());
EXPECT_EQ(parent_worker->client_frames().size(), 1u);
EXPECT_EQ(parent_worker->client_workers().size(), 0u);
EXPECT_EQ(parent_worker->child_workers().size(), 2u);
grandchild_worker->RemoveClientWorker(child_worker_1.get());
child_worker_2->RemoveClientWorker(parent_worker.get());
child_worker_1->RemoveClientWorker(parent_worker.get());
parent_worker->RemoveClientFrame(frame.get());
}
class TestWorkerNodeObserver : public WorkerNodeObserver {
public:
TestWorkerNodeObserver() = default;
~TestWorkerNodeObserver() override = default;
void OnWorkerNodeAdded(const WorkerNode* worker_node) override {
EXPECT_TRUE(client_frames_.insert({worker_node, {}}).second);
EXPECT_TRUE(client_workers_.insert({worker_node, {}}).second);
}
void OnBeforeWorkerNodeRemoved(const WorkerNode* worker_node) override {
EXPECT_TRUE(client_frames_.empty());
EXPECT_TRUE(client_workers_.empty());
EXPECT_EQ(client_frames_.erase(worker_node), 1u);
EXPECT_EQ(client_workers_.erase(worker_node), 1u);
}
void OnClientFrameAdded(const WorkerNode* worker_node,
const FrameNode* client_frame_node) override {
auto& client_frames = client_frames_.find(worker_node)->second;
EXPECT_TRUE(client_frames.insert(client_frame_node).second);
}
void OnBeforeClientFrameRemoved(const WorkerNode* worker_node,
const FrameNode* client_frame_node) override {
auto& client_frames = client_frames_.find(worker_node)->second;
EXPECT_EQ(client_frames.erase(client_frame_node), 1u);
}
void OnClientWorkerAdded(const WorkerNode* worker_node,
const WorkerNode* client_worker_node) override {
auto& client_workers = client_workers_.find(worker_node)->second;
EXPECT_TRUE(client_workers.insert(client_worker_node).second);
}
void OnBeforeClientWorkerRemoved(
const WorkerNode* worker_node,
const WorkerNode* client_worker_node) override {
auto& client_workers = client_workers_.find(worker_node)->second;
EXPECT_EQ(client_workers.erase(client_worker_node), 1u);
}
const base::flat_map<const WorkerNode*, base::flat_set<const FrameNode*>>&
client_frames() {
return client_frames_;
}
const base::flat_map<const WorkerNode*, base::flat_set<const WorkerNode*>>&
client_workers() {
return client_workers_;
}
private:
base::flat_map<const WorkerNode*, base::flat_set<const FrameNode*>>
client_frames_;
base::flat_map<const WorkerNode*, base::flat_set<const WorkerNode*>>
client_workers_;
DISALLOW_COPY_AND_ASSIGN(TestWorkerNodeObserver);
};
// Same as the AddWorkerNodes test, but the graph is verified through the
// WorkerNodeObserver interface.
TEST_F(WorkerNodeImplTest, Observer_AddWorkerNodes) {
TestWorkerNodeObserver worker_node_observer;
graph()->AddWorkerNodeObserver(&worker_node_observer);
auto process = CreateNode<ProcessNodeImpl>();
auto page = CreateNode<PageNodeImpl>();
auto frame = CreateFrameNodeAutoId(process.get(), page.get());
auto dedicated_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto shared_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kShared, process.get());
auto service_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kService, process.get());
dedicated_worker->AddClientFrame(frame.get());
shared_worker->AddClientFrame(frame.get());
service_worker->AddClientFrame(frame.get());
// 3 different workers observed.
EXPECT_EQ(worker_node_observer.client_frames().size(), 3u);
for (const auto& worker_and_client_frames :
worker_node_observer.client_frames()) {
// For each worker, check that |frame| is a client.
const base::flat_set<const FrameNode*>& client_frames =
worker_and_client_frames.second;
EXPECT_TRUE(client_frames.find(frame.get()) != client_frames.end());
}
// Remove client connections.
service_worker->RemoveClientFrame(frame.get());
shared_worker->RemoveClientFrame(frame.get());
dedicated_worker->RemoveClientFrame(frame.get());
graph()->RemoveWorkerNodeObserver(&worker_node_observer);
}
// Same as the ClientsOfServiceWorkers test, but the graph is verified through
// the WorkerNodeObserver interface.
TEST_F(WorkerNodeImplTest, Observer_ClientsOfServiceWorkers) {
TestWorkerNodeObserver worker_node_observer;
graph()->AddWorkerNodeObserver(&worker_node_observer);
auto process = CreateNode<ProcessNodeImpl>();
auto page = CreateNode<PageNodeImpl>();
auto frame = CreateFrameNodeAutoId(process.get(), page.get());
auto dedicated_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, process.get());
auto shared_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kShared, process.get());
auto service_worker = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kService, process.get());
service_worker->AddClientFrame(frame.get());
service_worker->AddClientWorker(dedicated_worker.get());
service_worker->AddClientWorker(shared_worker.get());
// 3 different workers observed.
EXPECT_EQ(worker_node_observer.client_frames().size(), 3u);
// Check clients of the service worker.
const base::flat_set<const FrameNode*>& client_frames =
worker_node_observer.client_frames().find(service_worker.get())->second;
EXPECT_TRUE(client_frames.find(frame.get()) != client_frames.end());
const base::flat_set<const WorkerNode*>& client_workers =
worker_node_observer.client_workers().find(service_worker.get())->second;
EXPECT_TRUE(client_workers.find(dedicated_worker.get()) !=
client_workers.end());
EXPECT_TRUE(client_workers.find(shared_worker.get()) != client_workers.end());
// Remove client connections.
service_worker->RemoveClientWorker(shared_worker.get());
service_worker->RemoveClientWorker(dedicated_worker.get());
service_worker->RemoveClientFrame(frame.get());
graph()->RemoveWorkerNodeObserver(&worker_node_observer);
}
} // namespace performance_manager