blob: 000865b6513b84c00c52eacc15e6c15d83ca54cb [file] [log] [blame]
// Copyright 2020 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.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_MEMORY_TEST_HELPERS_H_
#define COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_MEMORY_TEST_HELPERS_H_
#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "components/performance_manager/public/render_process_host_id.h"
#include "components/performance_manager/public/render_process_host_proxy.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
#include "components/performance_manager/test_support/performance_manager_test_harness.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/performance_manager/v8_detailed_memory_reporter.mojom.h"
namespace content {
class RenderFrameHost;
}
namespace performance_manager {
class FrameNodeImpl;
class PageNodeImpl;
class ProcessNodeImpl;
namespace v8_memory {
// A fake implementation of the mojo interface that reports memory measurement
// results.
class LenientMockV8DetailedMemoryReporter
: public blink::mojom::V8DetailedMemoryReporter {
public:
LenientMockV8DetailedMemoryReporter();
~LenientMockV8DetailedMemoryReporter() override;
LenientMockV8DetailedMemoryReporter(
const LenientMockV8DetailedMemoryReporter& other) = delete;
LenientMockV8DetailedMemoryReporter operator=(
const LenientMockV8DetailedMemoryReporter& other) = delete;
MOCK_METHOD(void,
GetV8MemoryUsage,
(Mode mode, GetV8MemoryUsageCallback callback),
(override));
void Bind(mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
pending_receiver);
private:
mojo::Receiver<blink::mojom::V8DetailedMemoryReporter> receiver_{this};
};
using MockV8DetailedMemoryReporter =
::testing::StrictMock<LenientMockV8DetailedMemoryReporter>;
// The mode enum used in test expectations.
using ExpectedMode = MockV8DetailedMemoryReporter::Mode;
// A base class with helper functions to set up test expectations and fake mojo
// connections for V8 memory tests. This can be composed with GraphTestHarness
// or PerformanceManagerTestHarness to make a full test environment.
class V8MemoryTestBase {
public:
// A default process ID to use in tests.
static constexpr RenderProcessHostId kTestProcessID =
RenderProcessHostId(0xFAB);
V8MemoryTestBase();
virtual ~V8MemoryTestBase();
// Adaptor that calls GetMainThreadTaskRunner for the test harness's task
// environment.
virtual scoped_refptr<base::SingleThreadTaskRunner>
GetMainThreadTaskRunner() = 0;
protected:
// Simulate a renderer process reporting the given |data| over the
// mojom::V8DetailedMemoryReporter interface using |callback|.
void ReplyWithData(
blink::mojom::PerProcessV8MemoryUsagePtr data,
MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback);
// Simulate a renderer process reporting the given |data| over the
// mojom::V8DetailedMemoryReporter interface using |callback|, after a delay
// of |delay|.
void DelayedReplyWithData(
const base::TimeDelta& delay,
blink::mojom::PerProcessV8MemoryUsagePtr data,
MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback);
// Add a test expectation that the GetV8MemoryUsage method of |mock_reporter|
// will be called with the mode parameter equal to |expected_mode|. When the
// method is called, |responder| will be invoked to call the method's
// response callback. (ReplyWithData and DelayedReplyWithData are examples of
// methods that could be bound into |responder| callbacks.)
void ExpectQuery(
MockV8DetailedMemoryReporter* mock_reporter,
base::RepeatingCallback<
void(MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback)>
responder,
ExpectedMode expected_mode = ExpectedMode::DEFAULT);
// Add a test expectation that the GetV8MemoryUsage method of |mock_reporter|
// will be called with the mode parameter equal to |expected_mode|. When the
// method is called, its response callback will be called with |data|.
void ExpectQueryAndReply(MockV8DetailedMemoryReporter* mock_reporter,
blink::mojom::PerProcessV8MemoryUsagePtr data,
ExpectedMode expected_mode = ExpectedMode::DEFAULT);
// Add a test expectation that the GetV8MemoryUsage method of |mock_reporter|
// will be called with the mode parameter equal to |expected_mode|. When the
// method is called, its response callback will be called with |data| after a
// delay of |delay|.
void ExpectQueryAndDelayReply(
MockV8DetailedMemoryReporter* mock_reporter,
const base::TimeDelta& delay,
blink::mojom::PerProcessV8MemoryUsagePtr data,
ExpectedMode expected_mode = ExpectedMode::DEFAULT);
// Add a test expectation that
// BindReceiverWithProxyHost will be called with the proxy parameter having ID
// |expected_process_id|.
void ExpectBindReceiver(
MockV8DetailedMemoryReporter* mock_reporter,
RenderProcessHostId expected_process_id = kTestProcessID);
// Add test expectations that two methods will be called in sequence:
// BindReceiverWithProxyHost (as in ExpectBindReceiver) and GetV8MemoryUsage
// (as in ExpectQueryAndReply). This is a useful shorthand because the
// receiver is always bound just before sending the first request to a
// process.
void ExpectBindAndRespondToQuery(
MockV8DetailedMemoryReporter* mock_reporter,
blink::mojom::PerProcessV8MemoryUsagePtr data,
RenderProcessHostId expected_process_id = kTestProcessID,
ExpectedMode expected_mode = ExpectedMode::DEFAULT);
// A mock method that will be called the first time a memory measurement is
// requested for a process.
MOCK_METHOD(void,
BindReceiverWithProxyHost,
(mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
pending_receiver,
RenderProcessHostProxy proxy),
(const));
// The last time one of the query expectations installed by the Expect*
// methods was fulfilled.
base::TimeTicks last_query_time() const { return last_query_time_; }
private:
// Invokes BindReceiverWithProxyHost on the main sequence.
void BindReceiverOnMainSequence(
mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
pending_receiver,
RenderProcessHostProxy proxy);
// A callback that will live as long as |this| does. Will be installed with
// SetBindV8DetailedMemoryReporterCallbackForTesting.
internal::BindV8DetailedMemoryReporterCallback bind_callback_;
base::TimeTicks last_query_time_;
};
// A PerformanceManagerTestHarness that exposes the helpers from
// V8MemoryTestBase and sets up some frames whose memory can be measured.
class V8MemoryPerformanceManagerTestHarness
: public PerformanceManagerTestHarness,
public V8MemoryTestBase {
public:
V8MemoryPerformanceManagerTestHarness();
~V8MemoryPerformanceManagerTestHarness() override;
static constexpr char kMainFrameUrl[] = "http://a.com/";
static constexpr char kChildFrameUrl[] = "http://b.com/";
void SetUp() override;
// The main frame, which always exists.
content::RenderFrameHost* main_frame() const { return main_frame_; }
// The ID of the main frame's renderer process.
RenderProcessHostId main_process_id() const { return main_process_id_; }
// A cross-process child frame. Will return nullptr unless
// CreateCrossProcessChildFrame is called.
content::RenderFrameHost* child_frame() const { return child_frame_; }
// The ID of the child frame's renderer process. Returns null if the child
// frame does not exist.
RenderProcessHostId child_process_id() const { return child_process_id_; }
// Creates a child frame that has its own renderer process. After calling
// this the frame can be accessed with child_frame() and child_process_id().
void CreateCrossProcessChildFrame();
// V8MemoryTestBase implementation.
scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner()
override;
private:
content::RenderFrameHost* main_frame_ = nullptr;
content::RenderFrameHost* child_frame_ = nullptr;
RenderProcessHostId main_process_id_;
RenderProcessHostId child_process_id_;
};
// A GraphTestHarness that adds convenience functions used by both
// WebMemoryImplTest and WebMemoryAggregatorTest.
class WebMemoryTestHarness : public GraphTestHarness {
public:
using Super = GraphTestHarness;
// Wrapper for memory usage bytes to improve test readability.
using Bytes = base::Optional<uint64_t>;
WebMemoryTestHarness();
~WebMemoryTestHarness() override;
void SetUp() override;
// Creates and adds a new frame node to the graph.
FrameNodeImpl* AddFrameNode(
std::string url,
Bytes bytes,
FrameNodeImpl* parent = nullptr,
base::Optional<std::string> id_attribute = base::nullopt,
base::Optional<std::string> src_attribute = base::nullopt) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent,
/*opener=*/nullptr, process_.get(), id_attribute,
src_attribute);
}
// Creates a frame node as if from window.open and adds it to the graph.
FrameNodeImpl* AddFrameNodeFromOpener(base::Optional<std::string> url,
Bytes bytes,
FrameNodeImpl* opener) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes,
/*parent=*/nullptr, opener, process_.get(),
/*id_attribute=*/base::nullopt,
/*src_attribute=*/base::nullopt);
}
// Creates a frame node in a different browsing instance and adds it to the
// graph.
FrameNodeImpl* AddCrossBrowsingInstanceFrameNode(
std::string url,
Bytes bytes,
FrameNodeImpl* parent = nullptr,
base::Optional<std::string> id_attribute = base::nullopt,
base::Optional<std::string> src_attribute = base::nullopt) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes, parent,
/*opener=*/nullptr, process_.get(), id_attribute,
src_attribute);
}
// Creates a frame node in a different browsing instance as if from
// window.open and adds it to the graph.
FrameNodeImpl* AddCrossBrowsingInstanceFrameNodeFromOpener(
std::string url,
Bytes bytes,
FrameNodeImpl* opener) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes,
/*parent=*/nullptr, opener, process_.get(),
/*id_attribute=*/base::nullopt,
/*src_attribute=*/base::nullopt);
}
// Creates a frame node in a different process and adds it to the graph.
FrameNodeImpl* AddCrossProcessFrameNode(
std::string url,
Bytes bytes,
FrameNodeImpl* parent,
base::Optional<std::string> id_attribute = base::nullopt,
base::Optional<std::string> src_attribute = base::nullopt) {
return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent,
/*opener=*/nullptr, other_process_.get(),
id_attribute, src_attribute);
}
WorkerNodeImpl* AddWorkerNode(WorkerNode::WorkerType worker_type,
std::string url,
Bytes bytes,
FrameNodeImpl* parent);
WorkerNodeImpl* AddWorkerNode(WorkerNode::WorkerType worker_type,
std::string url,
Bytes bytes,
WorkerNodeImpl* parent);
ProcessNode* process_node() const { return process_.get(); }
private:
static constexpr int kDefaultBrowsingInstanceId = 0;
// Creates and adds a new frame node to the graph.
FrameNodeImpl* AddFrameNodeImpl(base::Optional<std::string> url,
int browsing_instance_id,
Bytes bytes,
FrameNodeImpl* parent,
FrameNodeImpl* opener,
ProcessNodeImpl* process,
base::Optional<std::string> id_attribute,
base::Optional<std::string> src_attribute);
WorkerNodeImpl* AddWorkerNodeImpl(WorkerNode::WorkerType worker_type,
std::string url,
Bytes bytes);
int GetNextUniqueId();
TestNodeWrapper<ProcessNodeImpl> process_;
TestNodeWrapper<ProcessNodeImpl> other_process_;
std::vector<TestNodeWrapper<PageNodeImpl>> pages_;
std::vector<TestNodeWrapper<FrameNodeImpl>> frames_;
std::vector<TestNodeWrapper<WorkerNodeImpl>> workers_;
int next_unique_id_ = 0;
};
// Returns a new mojom::PerProcessV8MemoryUsage struct with
// |number_of_isolates| empty isolates.
blink::mojom::PerProcessV8MemoryUsagePtr NewPerProcessV8MemoryUsage(
size_t number_of_isolates);
// Finds the PerContextV8MemoryUsage in |isolate| whose token is |frame_token|,
// or creates it if it does not exist, and sets its bytes_used to |bytes_used|.
void AddIsolateMemoryUsage(const blink::LocalFrameToken& frame_token,
uint64_t bytes_used,
blink::mojom::PerIsolateV8MemoryUsage* isolate);
} // namespace v8_memory
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_MEMORY_TEST_HELPERS_H_