blob: 100d2eebb28dc3fd226dbbfdcfe37957b9658a3c [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.
#include "third_party/blink/renderer/controller/performance_manager/v8_worker_memory_reporter.h"
#include "base/time/time.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/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_test.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class V8WorkerMemoryReporterTest : public ::testing::Test {
public:
using Result = V8WorkerMemoryReporter::Result;
using WorkerMemoryUsage = V8WorkerMemoryReporter::WorkerMemoryUsage;
};
class V8WorkerMemoryReporterTestWithDedicatedWorker
: public DedicatedWorkerTest {};
class V8WorkerMemoryReporterTestWithMockPlatform
: public V8WorkerMemoryReporterTestWithDedicatedWorker {
public:
void SetUp() override {
EnablePlatform();
V8WorkerMemoryReporterTestWithDedicatedWorker::SetUp();
}
};
class MockCallback {
public:
MOCK_METHOD(void, Callback, (const V8WorkerMemoryReporter::Result&));
};
bool operator==(const V8WorkerMemoryReporter::WorkerMemoryUsage& lhs,
const V8WorkerMemoryReporter::WorkerMemoryUsage& rhs) {
return lhs.token == rhs.token && lhs.bytes == rhs.bytes;
}
bool operator==(const V8WorkerMemoryReporter::Result& lhs,
const V8WorkerMemoryReporter::Result& rhs) {
return lhs.workers == rhs.workers;
}
class MemoryUsageChecker {
public:
enum class CallbackAction { kExitRunLoop, kNone };
MemoryUsageChecker(size_t worker_count,
size_t bytes_per_worker_lower_bound,
CallbackAction callback_action)
: worker_count_(worker_count),
bytes_per_worker_lower_bound_(bytes_per_worker_lower_bound),
callback_action_(callback_action) {}
void Callback(const V8WorkerMemoryReporter::Result& result) {
EXPECT_EQ(worker_count_, result.workers.size());
size_t expected_counts[2] = {0, 1};
EXPECT_THAT(expected_counts, testing::Contains(worker_count_));
if (worker_count_ == 1) {
EXPECT_LE(bytes_per_worker_lower_bound_, result.workers[0].bytes);
EXPECT_EQ(KURL("http://fake.url/"), result.workers[0].url);
}
called_ = true;
if (callback_action_ == CallbackAction::kExitRunLoop) {
test::ExitRunLoop();
}
}
bool IsCalled() { return called_; }
private:
bool called_ = false;
size_t worker_count_;
size_t bytes_per_worker_lower_bound_;
CallbackAction callback_action_;
};
TEST_F(V8WorkerMemoryReporterTest, OnMeasurementSuccess) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(6);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2},
WorkerMemoryUsage{WorkerToken(SharedWorkerToken()), 3},
WorkerMemoryUsage{WorkerToken(SharedWorkerToken()), 4},
WorkerMemoryUsage{WorkerToken(ServiceWorkerToken()), 4},
WorkerMemoryUsage{WorkerToken(ServiceWorkerToken()), 5}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
for (auto& worker : result.workers) {
reporter.OnMeasurementSuccess(std::make_unique<WorkerMemoryUsage>(worker));
}
}
TEST_F(V8WorkerMemoryReporterTest, OnMeasurementFailure) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(3);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
reporter.OnMeasurementSuccess(
std::make_unique<WorkerMemoryUsage>(result.workers[0]));
reporter.OnMeasurementFailure();
reporter.OnMeasurementSuccess(
std::make_unique<WorkerMemoryUsage>(result.workers[1]));
}
TEST_F(V8WorkerMemoryReporterTest, OnTimeout) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(4);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
reporter.OnMeasurementSuccess(
std::make_unique<WorkerMemoryUsage>(result.workers[0]));
reporter.OnMeasurementSuccess(
std::make_unique<WorkerMemoryUsage>(result.workers[1]));
reporter.OnTimeout();
reporter.OnMeasurementSuccess(std::make_unique<WorkerMemoryUsage>(
WorkerMemoryUsage{WorkerToken(SharedWorkerToken()), 2}));
reporter.OnMeasurementFailure();
}
TEST_F(V8WorkerMemoryReporterTest, OnTimeoutNoop) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(2);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
reporter.OnMeasurementSuccess(
std::make_unique<WorkerMemoryUsage>(result.workers[0]));
reporter.OnMeasurementSuccess(
std::make_unique<WorkerMemoryUsage>(result.workers[1]));
reporter.OnTimeout();
}
TEST_F(V8WorkerMemoryReporterTestWithDedicatedWorker, GetMemoryUsage) {
const String source_code = "globalThis.array = new Array(1000000).fill(0);";
StartWorker(source_code);
WaitUntilWorkerIsRunning();
constexpr size_t kBytesPerArrayElement = 4;
constexpr size_t kArrayLength = 1000000;
MemoryUsageChecker checker(1, kBytesPerArrayElement * kArrayLength,
MemoryUsageChecker::CallbackAction::kExitRunLoop);
V8WorkerMemoryReporter::GetMemoryUsage(
WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker)),
v8::MeasureMemoryExecution::kEager);
test::EnterRunLoop();
EXPECT_TRUE(checker.IsCalled());
}
TEST_F(V8WorkerMemoryReporterTestWithMockPlatform, GetMemoryUsageTimeout) {
const String source_code = "while(true);";
StartWorker(source_code);
// Since the worker is in infinite loop and does not process tasks,
// we cannot call WaitUntilWorkerIsRunning here as that would block.
MemoryUsageChecker checker(0, 0, MemoryUsageChecker::CallbackAction::kNone);
V8WorkerMemoryReporter::GetMemoryUsage(
WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker)),
v8::MeasureMemoryExecution::kEager);
platform()->RunForPeriodSeconds(V8WorkerMemoryReporter::kTimeout.InSeconds() +
1);
EXPECT_TRUE(checker.IsCalled());
}
} // namespace blink