blob: 6b34213518ba83c3ac470e293a9f03fa4b986424 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/performance_manager/webui_graph_dump_impl.h"
#include <map>
#include <set>
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/test/bind_test_util.h"
#include "base/time/time.h"
#include "chrome/browser/performance_manager/graph/mock_graphs.h"
#include "chrome/browser/performance_manager/graph/page_node_impl.h"
#include "chrome/browser/performance_manager/performance_manager_clock.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace performance_manager {
namespace {
class TestChangeStream : public mojom::WebUIGraphChangeStream {
public:
using FrameMap = std::map<int64_t, mojom::WebUIFrameInfoPtr>;
using PageMap = std::map<int64_t, mojom::WebUIPageInfoPtr>;
using ProcessMap = std::map<int64_t, mojom::WebUIProcessInfoPtr>;
using IdSet = std::set<int64_t>;
TestChangeStream() : binding_(this) {}
mojom::WebUIGraphChangeStreamPtr GetProxy() {
mojom::WebUIGraphChangeStreamPtr proxy;
binding_.Bind(mojo::MakeRequest(&proxy));
return proxy;
}
// mojom::WebUIGraphChangeStream implementation
void FrameCreated(mojom::WebUIFrameInfoPtr frame) override {
EXPECT_FALSE(HasId(frame->id));
// If the node has a parent frame, we must have heard of it.
EXPECT_TRUE(HasIdIfValid(frame->parent_frame_id));
EXPECT_TRUE(HasId(frame->page_id));
EXPECT_TRUE(HasId(frame->process_id));
id_set_.insert(frame->id);
frame_map_.insert(std::make_pair(frame->id, std::move(frame)));
}
void PageCreated(mojom::WebUIPageInfoPtr page) override {
EXPECT_FALSE(HasId(page->id));
id_set_.insert(page->id);
page_map_.insert(std::make_pair(page->id, std::move(page)));
}
void ProcessCreated(mojom::WebUIProcessInfoPtr process) override {
EXPECT_FALSE(HasId(process->id));
id_set_.insert(process->id);
process_map_.insert(std::make_pair(process->id, std::move(process)));
}
void FrameChanged(mojom::WebUIFrameInfoPtr frame) override {
EXPECT_TRUE(HasId(frame->id));
frame_map_[frame->id] = std::move(frame);
++num_changes_;
}
void PageChanged(mojom::WebUIPageInfoPtr page) override {
EXPECT_TRUE(HasId(page->id));
page_map_[page->id] = std::move(page);
++num_changes_;
}
void ProcessChanged(mojom::WebUIProcessInfoPtr process) override {
EXPECT_TRUE(HasId(process->id));
process_map_[process->id] = std::move(process);
++num_changes_;
}
void FavIconDataAvailable(mojom::WebUIFavIconInfoPtr favicon) override {}
void NodeDeleted(int64_t node_id) override {
EXPECT_EQ(1u, id_set_.erase(node_id));
size_t erased = frame_map_.erase(node_id) + page_map_.erase(node_id) +
process_map_.erase(node_id);
EXPECT_EQ(1u, erased);
}
const FrameMap& frame_map() const { return frame_map_; }
const PageMap& page_map() const { return page_map_; }
const ProcessMap& process_map() const { return process_map_; }
const IdSet& id_set() const { return id_set_; }
size_t num_changes() const { return num_changes_; }
private:
bool HasId(int64_t id) { return base::ContainsKey(id_set_, id); }
bool HasIdIfValid(int64_t id) { return id == 0u || HasId(id); }
FrameMap frame_map_;
PageMap page_map_;
ProcessMap process_map_;
IdSet id_set_;
size_t num_changes_ = 0;
mojo::Binding<mojom::WebUIGraphChangeStream> binding_;
};
} // namespace
TEST(WebUIGraphDumpImplTest, ChangeStream) {
content::TestBrowserThreadBundle browser_thread_bundle;
GraphImpl graph;
MockMultiplePagesWithMultipleProcessesGraph mock_graph(&graph);
base::TimeTicks now = PerformanceManagerClock::NowTicks();
const GURL kExampleUrl("http://www.example.org");
mock_graph.page->OnMainFrameNavigationCommitted(now, 1, kExampleUrl);
mock_graph.other_page->OnMainFrameNavigationCommitted(now, 2, kExampleUrl);
auto* main_frame = mock_graph.page->GetMainFrameNode();
main_frame->OnNavigationCommitted(kExampleUrl, /* same_document */ false);
WebUIGraphDumpImpl impl(&graph);
// Create a mojo proxy to the impl.
mojom::WebUIGraphDumpPtr impl_proxy;
impl.Bind(mojo::MakeRequest(&impl_proxy), base::OnceClosure());
TestChangeStream change_stream;
impl_proxy->SubscribeToChanges(change_stream.GetProxy());
browser_thread_bundle.RunUntilIdle();
// Validate that the initial graph state dump is complete.
EXPECT_EQ(0u, change_stream.num_changes());
EXPECT_EQ(7u, change_stream.id_set().size());
EXPECT_EQ(2u, change_stream.process_map().size());
for (const auto& kv : change_stream.process_map()) {
EXPECT_NE(0u, kv.second->id);
}
EXPECT_EQ(3u, change_stream.frame_map().size());
// Count the top-level frames as we go.
size_t top_level_frames = 0;
for (const auto& kv : change_stream.frame_map()) {
const auto& frame = kv.second;
if (frame->parent_frame_id == 0) {
++top_level_frames;
// Top level frames should have a page ID.
EXPECT_NE(0u, frame->page_id);
// The page's main frame should have an URL.
if (frame->id == NodeBase::GetSerializationId(main_frame))
EXPECT_EQ(kExampleUrl, frame->url);
}
EXPECT_NE(0u, frame->id);
EXPECT_NE(0u, frame->process_id);
}
// Make sure we have one top-level frame per page.
EXPECT_EQ(change_stream.page_map().size(), top_level_frames);
EXPECT_EQ(2u, change_stream.page_map().size());
for (const auto& kv : change_stream.page_map()) {
const auto& page = kv.second;
EXPECT_NE(0u, page->id);
EXPECT_EQ(kExampleUrl, page->main_frame_url);
}
// Test change notifications.
const GURL kAnotherURL("http://www.google.com/");
mock_graph.page->OnMainFrameNavigationCommitted(now, 1, kAnotherURL);
size_t child_frame_id =
NodeBase::GetSerializationId(mock_graph.child_frame.get());
mock_graph.child_frame.reset();
browser_thread_bundle.RunUntilIdle();
EXPECT_EQ(1u, change_stream.num_changes());
EXPECT_FALSE(base::ContainsKey(change_stream.id_set(), child_frame_id));
const auto main_page_it = change_stream.page_map().find(
NodeBase::GetSerializationId(mock_graph.page.get()));
ASSERT_TRUE(main_page_it != change_stream.page_map().end());
EXPECT_EQ(kAnotherURL, main_page_it->second->main_frame_url);
browser_thread_bundle.RunUntilIdle();
}
} // namespace performance_manager