// Copyright 2016 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 <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/perf_time_logger.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/test/mojo_test_base.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace {
class EchoServiceImpl : public test::EchoService {
public:
explicit EchoServiceImpl(const base::Closure& quit_closure);
~EchoServiceImpl() override;
// |EchoService| methods:
void Echo(const std::string& test_data,
const EchoCallback& callback) override;
private:
const base::Closure quit_closure_;
};
EchoServiceImpl::EchoServiceImpl(const base::Closure& quit_closure)
: quit_closure_(quit_closure) {}
EchoServiceImpl::~EchoServiceImpl() {
quit_closure_.Run();
}
void EchoServiceImpl::Echo(const std::string& test_data,
const EchoCallback& callback) {
callback.Run(test_data);
}
class PingPongTest {
public:
explicit PingPongTest(test::EchoServicePtr service);
void RunTest(int iterations, int batch_size, int message_size);
private:
void DoPing();
void OnPingDone(const std::string& reply);
test::EchoServicePtr service_;
const base::Callback<void(const std::string&)> ping_done_callback_;
int iterations_;
int batch_size_;
std::string message_;
int current_iterations_;
int calls_outstanding_;
base::Closure quit_closure_;
};
PingPongTest::PingPongTest(test::EchoServicePtr service)
: service_(std::move(service)),
ping_done_callback_(
base::Bind(&PingPongTest::OnPingDone, base::Unretained(this))) {}
void PingPongTest::RunTest(int iterations, int batch_size, int message_size) {
iterations_ = iterations;
batch_size_ = batch_size;
message_ = std::string(message_size, 'a');
current_iterations_ = 0;
calls_outstanding_ = 0;
base::MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&PingPongTest::DoPing, base::Unretained(this)));
run_loop.Run();
}
void PingPongTest::DoPing() {
DCHECK_EQ(0, calls_outstanding_);
current_iterations_++;
if (current_iterations_ > iterations_) {
quit_closure_.Run();
return;
}
calls_outstanding_ = batch_size_;
for (int i = 0; i < batch_size_; i++) {
service_->Echo(message_, ping_done_callback_);
}
}
void PingPongTest::OnPingDone(const std::string& reply) {
DCHECK_GT(calls_outstanding_, 0);
calls_outstanding_--;
if (!calls_outstanding_)
DoPing();
}
class MojoE2EPerftest : public core::test::MojoTestBase {
public:
void RunTestOnTaskRunner(base::TaskRunner* runner,
MojoHandle client_mp,
const std::string& test_name) {
if (runner == base::ThreadTaskRunnerHandle::Get().get()) {
RunTests(client_mp, test_name);
} else {
base::RunLoop run_loop;
runner->PostTaskAndReply(
FROM_HERE,
base::Bind(&MojoE2EPerftest::RunTests, base::Unretained(this),
client_mp, test_name),
run_loop.QuitClosure());
run_loop.Run();
}
}
protected:
base::MessageLoop message_loop_;
private:
void RunTests(MojoHandle client_mp, const std::string& test_name) {
const int kMessages = 10000;
const int kBatchSizes[] = {1, 10, 100};
const int kMessageSizes[] = {8, 64, 512, 4096, 65536};
test::EchoServicePtr service;
service.Bind(InterfacePtrInfo<test::EchoService>(
ScopedMessagePipeHandle(MessagePipeHandle(client_mp)),
service.version()));
PingPongTest test(std::move(service));
for (int batch_size : kBatchSizes) {
for (int message_size : kMessageSizes) {
int num_messages = kMessages;
if (message_size == 65536)
num_messages /= 10;
std::string sub_test_name = base::StringPrintf(
"%s/%dx%d/%dbytes", test_name.c_str(), num_messages / batch_size,
batch_size, message_size);
base::PerfTimeLogger timer(sub_test_name.c_str());
test.RunTest(num_messages / batch_size, batch_size, message_size);
}
}
}
};
void CreateAndRunService(InterfaceRequest<test::EchoService> request,
const base::Closure& cb) {
MakeStrongBinding(std::make_unique<EchoServiceImpl>(cb), std::move(request));
}
DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingService, MojoE2EPerftest, mp) {
MojoHandle service_mp;
EXPECT_EQ("hello", ReadMessageWithHandles(mp, &service_mp, 1));
auto request = InterfaceRequest<test::EchoService>(
ScopedMessagePipeHandle(MessagePipeHandle(service_mp)));
base::RunLoop run_loop;
core::GetIOTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CreateAndRunService, base::Passed(&request),
base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
message_loop_.task_runner(), FROM_HERE,
run_loop.QuitClosure())));
run_loop.Run();
}
TEST_F(MojoE2EPerftest, MultiProcessEchoMainThread) {
RunTestClient("PingService", [&](MojoHandle mp) {
MojoHandle client_mp, service_mp;
CreateMessagePipe(&client_mp, &service_mp);
WriteMessageWithHandles(mp, "hello", &service_mp, 1);
RunTestOnTaskRunner(message_loop_.task_runner().get(), client_mp,
"MultiProcessEchoMainThread");
});
}
TEST_F(MojoE2EPerftest, MultiProcessEchoIoThread) {
RunTestClient("PingService", [&](MojoHandle mp) {
MojoHandle client_mp, service_mp;
CreateMessagePipe(&client_mp, &service_mp);
WriteMessageWithHandles(mp, "hello", &service_mp, 1);
RunTestOnTaskRunner(core::GetIOTaskRunner().get(), client_mp,
"MultiProcessEchoIoThread");
});
}
} // namespace
} // namespace mojo