blob: fe6fcd7e0b0186fd159b0cd9cdaad1a1e5b50fcb [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/cpp/bindings/remote.h"
#include <stdint.h>
#include <optional>
#include <tuple>
#include <utility>
#include "base/barrier_closure.h"
#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "mojo/core/test/mojo_test_base.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/bindings/shared_associated_remote.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "mojo/public/cpp/bindings/tests/bindings_test_base.h"
#include "mojo/public/cpp/bindings/tests/remote_unittest.test-mojom.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
#include "mojo/public/cpp/system/wait.h"
#include "mojo/public/interfaces/bindings/tests/math_calculator.test-mojom.h"
#include "mojo/public/interfaces/bindings/tests/sample_interfaces.test-mojom.h"
#include "mojo/public/interfaces/bindings/tests/sample_service.test-mojom.h"
#include "mojo/public/interfaces/bindings/tests/scoping.test-mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace remote_unittest {
namespace {
class MathCalculatorImpl : public math::Calculator {
public:
explicit MathCalculatorImpl(PendingReceiver<math::Calculator> receiver)
: total_(0.0), receiver_(this, std::move(receiver)) {}
~MathCalculatorImpl() override = default;
void Clear(ClearCallback callback) override {
total_ = 0.0;
std::move(callback).Run(total_);
}
void Add(double value, AddCallback callback) override {
total_ += value;
std::move(callback).Run(total_);
}
void Multiply(double value, MultiplyCallback callback) override {
total_ *= value;
std::move(callback).Run(total_);
}
Receiver<math::Calculator>& receiver() { return receiver_; }
double total() const { return total_; }
private:
double total_;
Receiver<math::Calculator> receiver_;
};
class MathCalculatorUI {
public:
explicit MathCalculatorUI(PendingRemote<math::Calculator> calculator)
: calculator_(std::move(calculator)), output_(0.0) {}
bool is_connected() const { return calculator_.is_connected(); }
void set_disconnect_handler(base::OnceClosure closure) {
calculator_.set_disconnect_handler(std::move(closure));
}
void Add(double value, base::OnceClosure closure) {
calculator_->Add(
value, base::BindOnce(&MathCalculatorUI::Output, base::Unretained(this),
std::move(closure)));
}
void Multiply(double value, base::OnceClosure closure) {
calculator_->Multiply(
value, base::BindOnce(&MathCalculatorUI::Output, base::Unretained(this),
std::move(closure)));
}
double GetOutput() const { return output_; }
Remote<math::Calculator>& remote() { return calculator_; }
private:
void Output(base::OnceClosure closure, double output) {
output_ = output;
if (closure)
std::move(closure).Run();
}
Remote<math::Calculator> calculator_;
double output_;
};
class SelfDestructingMathCalculatorUI {
public:
explicit SelfDestructingMathCalculatorUI(
PendingRemote<math::Calculator> calculator)
: calculator_(std::move(calculator)), nesting_level_(0) {
++num_instances_;
}
void BeginTest(bool nested, base::OnceClosure closure) {
nesting_level_ = nested ? 2 : 1;
calculator_->Add(
1.0, base::BindOnce(&SelfDestructingMathCalculatorUI::Output,
base::Unretained(this), std::move(closure)));
}
static int num_instances() { return num_instances_; }
void Output(base::OnceClosure closure, double value) {
if (--nesting_level_ > 0) {
// Add some more and wait for re-entrant call to Output!
calculator_->Add(
1.0, base::BindOnce(&SelfDestructingMathCalculatorUI::Output,
base::Unretained(this), std::move(closure)));
} else {
std::move(closure).Run();
delete this;
}
}
private:
~SelfDestructingMathCalculatorUI() { --num_instances_; }
Remote<math::Calculator> calculator_;
int nesting_level_;
static int num_instances_;
};
// static
int SelfDestructingMathCalculatorUI::num_instances_ = 0;
class ReentrantServiceImpl : public sample::Service {
public:
~ReentrantServiceImpl() override = default;
explicit ReentrantServiceImpl(PendingReceiver<sample::Service> receiver)
: call_depth_(0),
max_call_depth_(0),
receiver_(this, std::move(receiver)) {}
int max_call_depth() { return max_call_depth_; }
void Frobinate(sample::FooPtr foo,
sample::Service::BazOptions baz,
PendingRemote<sample::Port> port,
sample::Service::FrobinateCallback callback) override {
max_call_depth_ = std::max(++call_depth_, max_call_depth_);
if (call_depth_ == 1) {
EXPECT_TRUE(receiver_.WaitForIncomingCall());
}
call_depth_--;
std::move(callback).Run(5);
}
void GetPort(PendingReceiver<sample::Port> receiver) override {}
private:
int call_depth_;
int max_call_depth_;
Receiver<sample::Service> receiver_;
};
class IntegerAccessorImpl : public sample::IntegerAccessor {
public:
IntegerAccessorImpl() : integer_(0) {}
~IntegerAccessorImpl() override = default;
int64_t integer() const { return integer_; }
void set_closure(base::OnceClosure closure) { closure_ = std::move(closure); }
private:
// sample::IntegerAccessor implementation.
void GetInteger(GetIntegerCallback callback) override {
std::move(callback).Run(integer_, sample::Enum::VALUE);
}
void SetInteger(int64_t data, sample::Enum type) override {
integer_ = data;
if (closure_)
std::move(closure_).Run();
}
int64_t integer_;
base::OnceClosure closure_;
};
class RemoteTest : public BindingsTestBase {
public:
RemoteTest() = default;
~RemoteTest() override { base::RunLoop().RunUntilIdle(); }
void PumpMessages() { base::RunLoop().RunUntilIdle(); }
};
TEST_P(RemoteTest, IsBound) {
Remote<math::Calculator> calc;
EXPECT_FALSE(calc);
MathCalculatorImpl calc_impl(calc.BindNewPipeAndPassReceiver());
EXPECT_TRUE(calc);
}
class EndToEndRemoteTest : public RemoteTest {
public:
void RunTest(const scoped_refptr<base::SequencedTaskRunner> runner) {
base::RunLoop run_loop;
done_closure_ = run_loop.QuitClosure();
done_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
runner->PostTask(FROM_HERE, base::BindOnce(&EndToEndRemoteTest::RunTestImpl,
base::Unretained(this)));
run_loop.Run();
}
private:
void RunTestImpl() {
PendingRemote<math::Calculator> calc;
calc_impl_ = std::make_unique<MathCalculatorImpl>(
calc.InitWithNewPipeAndPassReceiver());
calculator_ui_ = std::make_unique<MathCalculatorUI>(std::move(calc));
calculator_ui_->Add(2.0, base::BindOnce(&EndToEndRemoteTest::AddDone,
base::Unretained(this)));
calculator_ui_->Multiply(5.0,
base::BindOnce(&EndToEndRemoteTest::MultiplyDone,
base::Unretained(this)));
EXPECT_EQ(0.0, calculator_ui_->GetOutput());
}
void AddDone() { EXPECT_EQ(2.0, calculator_ui_->GetOutput()); }
void MultiplyDone() {
EXPECT_EQ(10.0, calculator_ui_->GetOutput());
calculator_ui_.reset();
calc_impl_.reset();
done_runner_->PostTask(FROM_HERE, std::move(done_closure_));
}
base::OnceClosure done_closure_;
scoped_refptr<base::SequencedTaskRunner> done_runner_;
std::unique_ptr<MathCalculatorUI> calculator_ui_;
std::unique_ptr<MathCalculatorImpl> calc_impl_;
};
TEST_P(EndToEndRemoteTest, EndToEnd) {
RunTest(base::SingleThreadTaskRunner::GetCurrentDefault());
}
TEST_P(EndToEndRemoteTest, EndToEndOnSequence) {
RunTest(base::ThreadPool::CreateSequencedTaskRunner({}));
}
TEST_P(RemoteTest, Movable) {
Remote<math::Calculator> a;
Remote<math::Calculator> b;
MathCalculatorImpl calc_impl(b.BindNewPipeAndPassReceiver());
EXPECT_TRUE(!a);
EXPECT_FALSE(!b);
a = std::move(b);
EXPECT_FALSE(!a);
EXPECT_TRUE(!b);
}
TEST_P(RemoteTest, Resettable) {
Remote<math::Calculator> a;
EXPECT_FALSE(a);
MessagePipe pipe;
// Save this so we can test it later.
Handle handle = pipe.handle0.get();
a.Bind(PendingRemote<math::Calculator>(std::move(pipe.handle0), 0u));
EXPECT_TRUE(a);
a.reset();
EXPECT_FALSE(a);
// Test that handle was closed by waiting for its peer to signal.
Wait(pipe.handle1.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED);
}
TEST_P(RemoteTest, InvalidPendingRemotes) {
PendingRemote<math::Calculator> invalid_remote;
EXPECT_FALSE(invalid_remote);
// A "null" remote is just a generic helper for an uninitialized
// PendingRemote. Verify that it's equivalent to above.
PendingRemote<math::Calculator> null_remote{NullRemote()};
EXPECT_FALSE(null_remote);
}
TEST_P(RemoteTest, IsConnected) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
MathCalculatorUI calculator_ui(std::move(remote));
base::RunLoop run_loop;
calculator_ui.Add(2.0, run_loop.QuitClosure());
run_loop.Run();
EXPECT_EQ(2.0, calculator_ui.GetOutput());
EXPECT_TRUE(calculator_ui.is_connected());
calculator_ui.Multiply(5.0, base::OnceClosure());
EXPECT_TRUE(calculator_ui.is_connected());
calc_impl.receiver().reset();
base::RunLoop run_loop2;
calculator_ui.set_disconnect_handler(run_loop2.QuitClosure());
// The state change isn't picked up locally yet.
EXPECT_TRUE(calculator_ui.is_connected());
run_loop2.Run();
// OK, now we see the disconnection.
EXPECT_FALSE(calculator_ui.is_connected());
}
TEST_P(RemoteTest, DisconnectCallback) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
MathCalculatorUI calculator_ui(std::move(remote));
bool connected = true;
base::RunLoop run_loop;
calculator_ui.remote().set_disconnect_handler(base::BindLambdaForTesting([&] {
connected = false;
run_loop.Quit();
}));
base::RunLoop run_loop2;
calculator_ui.Add(2.0, run_loop2.QuitClosure());
run_loop2.Run();
EXPECT_EQ(2.0, calculator_ui.GetOutput());
EXPECT_TRUE(calculator_ui.is_connected());
calculator_ui.Multiply(5.0, base::OnceClosure());
EXPECT_TRUE(calculator_ui.is_connected());
calc_impl.receiver().reset();
// The state change isn't picked up locally yet.
EXPECT_TRUE(calculator_ui.is_connected());
run_loop.Run();
// OK, now we see the disconnection.
EXPECT_FALSE(calculator_ui.is_connected());
// We should have also been able to observe the error through the error
// handler.
EXPECT_FALSE(connected);
}
TEST_P(RemoteTest, DestroyRemoteOnMethodResponse) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
SelfDestructingMathCalculatorUI* impl =
new SelfDestructingMathCalculatorUI(std::move(remote));
base::RunLoop run_loop;
impl->BeginTest(false, run_loop.QuitClosure());
run_loop.Run();
EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
}
TEST_P(RemoteTest, NestedDestroyRemoteOnMethodResponse) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
SelfDestructingMathCalculatorUI* impl =
new SelfDestructingMathCalculatorUI(std::move(remote));
base::RunLoop run_loop;
impl->BeginTest(true, run_loop.QuitClosure());
run_loop.Run();
EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
}
TEST_P(RemoteTest, ReentrantWaitForIncomingCall) {
Remote<sample::Service> remote;
ReentrantServiceImpl impl(remote.BindNewPipeAndPassReceiver());
base::RunLoop run_loop, run_loop2;
remote->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, NullRemote(),
base::BindLambdaForTesting([&](int) { run_loop.Quit(); }));
remote->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, NullRemote(),
base::BindLambdaForTesting([&](int) { run_loop2.Quit(); }));
run_loop.Run();
run_loop2.Run();
EXPECT_EQ(2, impl.max_call_depth());
}
TEST_P(RemoteTest, QueryVersion) {
IntegerAccessorImpl impl;
Remote<sample::IntegerAccessor> remote;
Receiver<sample::IntegerAccessor> receiver(
&impl, remote.BindNewPipeAndPassReceiver());
EXPECT_EQ(0u, remote.version());
base::RunLoop run_loop;
remote.QueryVersion(base::BindLambdaForTesting([&](uint32_t version) {
EXPECT_EQ(3u, version);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(3u, remote.version());
}
TEST_P(RemoteTest, RequireVersion) {
IntegerAccessorImpl impl;
Remote<sample::IntegerAccessor> remote;
Receiver<sample::IntegerAccessor> receiver(
&impl, remote.BindNewPipeAndPassReceiver());
EXPECT_EQ(0u, remote.version());
remote.RequireVersion(1u);
EXPECT_EQ(1u, remote.version());
base::RunLoop run_loop;
impl.set_closure(run_loop.QuitClosure());
remote->SetInteger(123, sample::Enum::VALUE);
run_loop.Run();
EXPECT_TRUE(remote.is_connected());
EXPECT_EQ(123, impl.integer());
remote.RequireVersion(3u);
EXPECT_EQ(3u, remote.version());
base::RunLoop run_loop2;
impl.set_closure(run_loop2.QuitClosure());
remote->SetInteger(456, sample::Enum::VALUE);
run_loop2.Run();
EXPECT_TRUE(remote.is_connected());
EXPECT_EQ(456, impl.integer());
// Require a version that is not supported by the impl side.
remote.RequireVersion(4u);
// This value is set to the input of RequireVersion() synchronously.
EXPECT_EQ(4u, remote.version());
base::RunLoop run_loop3;
remote.set_disconnect_handler(run_loop3.QuitClosure());
remote->SetInteger(789, sample::Enum::VALUE);
run_loop3.Run();
EXPECT_FALSE(remote.is_connected());
// The call to SetInteger() after RequireVersion(4u) is ignored.
EXPECT_EQ(456, impl.integer());
}
class StrongMathCalculatorImpl : public math::Calculator {
public:
StrongMathCalculatorImpl(bool* destroyed) : destroyed_(destroyed) {}
~StrongMathCalculatorImpl() override { *destroyed_ = true; }
// math::Calculator implementation.
void Clear(ClearCallback callback) override {
std::move(callback).Run(total_);
}
void Add(double value, AddCallback callback) override {
total_ += value;
std::move(callback).Run(total_);
}
void Multiply(double value, MultiplyCallback callback) override {
total_ *= value;
std::move(callback).Run(total_);
}
private:
double total_ = 0.0;
raw_ptr<bool> destroyed_;
};
TEST(StrongConnectorTest, Math) {
base::test::SingleThreadTaskEnvironment task_environment;
bool disconnected = false;
bool destroyed = false;
PendingRemote<math::Calculator> calc;
base::RunLoop run_loop;
UniqueReceiverSet<math::Calculator> receivers;
receivers.Add(std::make_unique<StrongMathCalculatorImpl>(&destroyed),
calc.InitWithNewPipeAndPassReceiver());
receivers.set_disconnect_handler(base::BindLambdaForTesting([&] {
disconnected = true;
run_loop.Quit();
}));
{
MathCalculatorUI calculator_ui(std::move(calc));
base::RunLoop run_loop2, run_loop3;
calculator_ui.Add(2.0, run_loop2.QuitClosure());
calculator_ui.Multiply(5.0, run_loop3.QuitClosure());
run_loop2.Run();
run_loop3.Run();
EXPECT_EQ(10.0, calculator_ui.GetOutput());
EXPECT_FALSE(disconnected);
EXPECT_FALSE(destroyed);
}
// Destroying calculator_ui should close the pipe and signal disconnection on
// the receiving end, which will in turn destroy the impl since it's in a
// UniqueReceiverSet.
run_loop.Run();
EXPECT_TRUE(disconnected);
EXPECT_TRUE(destroyed);
}
class WeakMathCalculatorImpl : public math::Calculator {
public:
WeakMathCalculatorImpl(PendingReceiver<math::Calculator> receiver,
bool* disconnected,
bool* destroyed,
base::OnceClosure closure)
: destroyed_(destroyed), receiver_(this, std::move(receiver)) {
receiver_.set_disconnect_handler(base::BindOnce(
[](bool* disconnected, base::OnceClosure closure) {
*disconnected = true;
std::move(closure).Run();
},
disconnected, std::move(closure)));
}
~WeakMathCalculatorImpl() override { *destroyed_ = true; }
void Clear(ClearCallback callback) override {
std::move(callback).Run(total_);
}
void Add(double value, AddCallback callback) override {
total_ += value;
std::move(callback).Run(total_);
}
void Multiply(double value, MultiplyCallback callback) override {
total_ *= value;
std::move(callback).Run(total_);
}
private:
double total_ = 0.0;
raw_ptr<bool> destroyed_;
base::OnceClosure closure_;
Receiver<math::Calculator> receiver_;
};
TEST(WeakConnectorTest, Math) {
base::test::SingleThreadTaskEnvironment task_environment;
bool disconnected = false;
bool destroyed = false;
MessagePipe pipe;
base::RunLoop run_loop;
WeakMathCalculatorImpl impl(
PendingReceiver<math::Calculator>(std::move(pipe.handle0)), &disconnected,
&destroyed, run_loop.QuitClosure());
{
MathCalculatorUI calculator_ui(
PendingRemote<math::Calculator>(std::move(pipe.handle1), 0u));
base::RunLoop run_loop2, run_loop3;
calculator_ui.Add(2.0, run_loop2.QuitClosure());
calculator_ui.Multiply(5.0, run_loop3.QuitClosure());
run_loop2.Run();
run_loop3.Run();
EXPECT_EQ(10.0, calculator_ui.GetOutput());
EXPECT_FALSE(disconnected);
EXPECT_FALSE(destroyed);
}
run_loop.Run();
EXPECT_TRUE(disconnected);
EXPECT_FALSE(destroyed);
}
class CImpl : public C {
public:
CImpl(bool* d_called, base::OnceClosure closure)
: d_called_(d_called), closure_(std::move(closure)) {}
~CImpl() override = default;
void Bind(PendingReceiver<C> receiver) {
receiver_.Bind(std::move(receiver));
}
private:
void D() override {
*d_called_ = true;
std::move(closure_).Run();
}
Receiver<C> receiver_{this};
raw_ptr<bool> d_called_;
base::OnceClosure closure_;
};
class BImpl : public B {
public:
BImpl(bool* d_called, base::OnceClosure closure)
: c_(d_called, std::move(closure)) {}
~BImpl() override = default;
void Bind(PendingReceiver<B> receiver) {
receiver_.Bind(std::move(receiver));
}
private:
void GetC(PendingReceiver<C> receiver) override {
c_.Bind(std::move(receiver));
}
Receiver<B> receiver_{this};
CImpl c_;
};
class AImpl : public A {
public:
AImpl(PendingReceiver<A> receiver, base::OnceClosure closure)
: d_called_(false),
receiver_(this, std::move(receiver)),
b_(&d_called_, std::move(closure)) {}
~AImpl() override = default;
bool d_called() const { return d_called_; }
private:
void GetB(PendingReceiver<B> receiver) override {
b_.Bind(std::move(receiver));
}
bool d_called_;
Receiver<A> receiver_;
BImpl b_;
};
TEST_P(RemoteTest, Scoping) {
Remote<A> a;
base::RunLoop run_loop;
AImpl a_impl(a.BindNewPipeAndPassReceiver(), run_loop.QuitClosure());
EXPECT_FALSE(a_impl.d_called());
{
Remote<B> b;
a->GetB(b.BindNewPipeAndPassReceiver());
Remote<C> c;
b->GetC(c.BindNewPipeAndPassReceiver());
c->D();
}
// While B & C have fallen out of scope, the receiving endpoints will continue
// to operate, and any messages sent prior to destruction will be delivered.
EXPECT_FALSE(a_impl.d_called());
run_loop.Run();
EXPECT_TRUE(a_impl.d_called());
}
class PingTestImpl : public sample::PingTest {
public:
explicit PingTestImpl(PendingReceiver<sample::PingTest> receiver)
: receiver_(this, std::move(receiver)) {}
~PingTestImpl() override = default;
private:
// sample::PingTest:
void Ping(PingCallback callback) override { std::move(callback).Run(); }
Receiver<sample::PingTest> receiver_;
};
// Tests that FuseProxy does what it's supposed to do.
TEST_P(RemoteTest, Fusion) {
PendingRemote<sample::PingTest> pending_remote;
PingTestImpl impl(pending_remote.InitWithNewPipeAndPassReceiver());
// Create another PingTest pipe and fuse it to the one hanging off |impl|.
Remote<sample::PingTest> remote;
EXPECT_TRUE(FusePipes(remote.BindNewPipeAndPassReceiver(),
std::move(pending_remote)));
// Ping!
bool called = false;
base::RunLoop loop;
remote->Ping(base::BindLambdaForTesting([&] {
called = true;
loop.Quit();
}));
loop.Run();
EXPECT_TRUE(called);
}
void Fail() {
FAIL() << "Unexpected connection error";
}
TEST_P(RemoteTest, FlushForTesting) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
MathCalculatorUI calculator_ui(std::move(remote));
calculator_ui.remote().set_disconnect_handler(base::BindOnce(&Fail));
calculator_ui.Add(2.0, base::DoNothing());
calculator_ui.remote().FlushForTesting();
EXPECT_EQ(2.0, calculator_ui.GetOutput());
calculator_ui.Multiply(5.0, base::DoNothing());
calculator_ui.remote().FlushForTesting();
EXPECT_EQ(10.0, calculator_ui.GetOutput());
}
TEST_P(RemoteTest, FlushAsyncForTesting) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
MathCalculatorUI calculator_ui(std::move(remote));
calculator_ui.remote().set_disconnect_handler(base::BindOnce(&Fail));
calculator_ui.Add(2.0, base::DoNothing());
base::RunLoop run_loop;
calculator_ui.remote().FlushAsyncForTesting(run_loop.QuitClosure());
run_loop.Run();
EXPECT_EQ(2.0, calculator_ui.GetOutput());
calculator_ui.Multiply(5.0, base::DoNothing());
base::RunLoop run_loop2;
calculator_ui.remote().FlushAsyncForTesting(run_loop2.QuitClosure());
run_loop2.Run();
EXPECT_EQ(10.0, calculator_ui.GetOutput());
}
TEST_P(RemoteTest, FlushForTestingWithClosedPeer) {
Remote<math::Calculator> calc;
std::ignore = calc.BindNewPipeAndPassReceiver();
bool called = false;
calc.set_disconnect_handler(
base::BindLambdaForTesting([&] { called = true; }));
calc.FlushForTesting();
EXPECT_TRUE(called);
calc.FlushForTesting();
}
TEST_P(RemoteTest, FlushAsyncForTestingWithClosedPeer) {
Remote<math::Calculator> calc;
std::ignore = calc.BindNewPipeAndPassReceiver();
bool called = false;
calc.set_disconnect_handler(
base::BindLambdaForTesting([&] { called = true; }));
base::RunLoop run_loop;
calc.FlushAsyncForTesting(run_loop.QuitClosure());
run_loop.Run();
EXPECT_TRUE(called);
base::RunLoop run_loop2;
calc.FlushAsyncForTesting(run_loop2.QuitClosure());
run_loop2.Run();
}
TEST_P(RemoteTest, DisconnectWithReason) {
Remote<math::Calculator> calc;
MathCalculatorImpl calc_impl(calc.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
calc.set_disconnect_with_reason_handler(base::BindLambdaForTesting(
[&](uint32_t custom_reason, const std::string& description) {
EXPECT_EQ(42u, custom_reason);
EXPECT_EQ("hey", description);
run_loop.Quit();
}));
calc_impl.receiver().ResetWithReason(42u, "hey");
run_loop.Run();
}
TEST_P(RemoteTest, PendingReceiverResetWithReason) {
Remote<math::Calculator> calc;
auto pending_receiver = calc.BindNewPipeAndPassReceiver();
base::RunLoop run_loop;
calc.set_disconnect_with_reason_handler(base::BindLambdaForTesting(
[&](uint32_t custom_reason, const std::string& description) {
EXPECT_EQ(88u, custom_reason);
EXPECT_EQ("greetings", description);
run_loop.Quit();
}));
pending_receiver.ResetWithReason(88u, "greetings");
run_loop.Run();
}
TEST_P(RemoteTest, PendingReceiverResetWithReasonAfterDisconnect) {
Remote<math::Calculator> calc;
auto pending_receiver = calc.BindNewPipeAndPassReceiver();
calc.reset();
// Ensure no crashes occur when ResetWithReason is called after the other
// side has disconnected.
pending_receiver.ResetWithReason(0u, "not-used");
}
TEST_P(RemoteTest, CallbackIsPassedRemote) {
Remote<sample::PingTest> remote;
auto pending_receiver = remote.BindNewPipeAndPassReceiver();
base::RunLoop run_loop;
// Make a call with the proxy's lifetime bound to the response callback.
sample::PingTest* raw_proxy = remote.get();
remote.set_disconnect_handler(run_loop.QuitClosure());
raw_proxy->Ping(
base::BindOnce([](Remote<sample::PingTest>) {}, std::move(remote)));
// Signal disconnection on |remote|. This will ultimately lead to the proxy's
// response callbacks being destroyed, which will in turn lead to the proxy
// being destroyed. This should not crash.
pending_receiver.reset();
run_loop.Run();
}
TEST_P(RemoteTest, DisconnectHandlerOwnsRemote) {
Remote<sample::PingTest>* remote = new Remote<sample::PingTest>;
auto pending_receiver = remote->BindNewPipeAndPassReceiver();
base::RunLoop run_loop;
// Make a call with |remote|'s lifetime bound to the disconnect handler.
remote->set_disconnect_handler(base::BindOnce(
[](base::OnceClosure quit, Remote<sample::PingTest>* owned_remote) {
owned_remote->reset();
std::move(quit).Run();
},
run_loop.QuitClosure(), base::Owned(remote)));
// Signal disconnection on |remote|. In the disconnect handler |remote| is
// reset. This shouldn't immediately destroy the callback (and |remote| that
// it owns), before the callback is completed.
pending_receiver.reset();
run_loop.Run();
}
TEST_P(RemoteTest, SharedRemote) {
PendingRemote<math::Calculator> pending_remote;
MathCalculatorImpl calc_impl(pending_remote.InitWithNewPipeAndPassReceiver());
SharedRemote<math::Calculator> shared_remote(std::move(pending_remote));
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
// Send a message on |thread_safe_remote| from a different sequence.
auto main_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
auto sender_task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
sender_task_runner->PostTask(
FROM_HERE, base::BindLambdaForTesting([&] {
shared_remote->Add(
123, base::BindLambdaForTesting([&](double result) {
EXPECT_EQ(123, result);
// Validate the callback is invoked on the calling sequence.
EXPECT_TRUE(sender_task_runner->RunsTasksInCurrentSequence());
main_task_runner->PostTask(FROM_HERE, std::move(quit_closure));
}));
}));
run_loop.Run();
}
TEST_P(RemoteTest, SharedRemoteWithTaskRunner) {
const scoped_refptr<base::SequencedTaskRunner> other_thread_task_runner =
base::ThreadPool::CreateSequencedTaskRunner({});
PendingRemote<math::Calculator> remote;
auto receiver = remote.InitWithNewPipeAndPassReceiver();
// Create a ThreadSafeRemote that we'll bind from a different thread.
SharedRemote<math::Calculator> shared_remote(std::move(remote),
other_thread_task_runner);
ASSERT_TRUE(shared_remote);
MathCalculatorImpl* math_calc_impl = nullptr;
{
base::RunLoop run_loop;
auto quit_closure = run_loop.QuitClosure();
other_thread_task_runner->PostTask(
FROM_HERE, base::BindLambdaForTesting([&] {
math_calc_impl = new MathCalculatorImpl(std::move(receiver));
std::move(quit_closure).Run();
}));
run_loop.Run();
}
{
base::RunLoop run_loop;
shared_remote->Add(123, base::BindLambdaForTesting([&](double result) {
EXPECT_EQ(123, result);
run_loop.Quit();
}));
run_loop.Run();
}
other_thread_task_runner->DeleteSoon(FROM_HERE, math_calc_impl);
// Reset the SharedRemote now so the background thread state tied to its
// internal Remote can be deleted before the background thread itself is
// cleaned up.
shared_remote.reset();
}
class SequenceCheckerImpl : public mojom::SequenceChecker {
public:
SequenceCheckerImpl() = default;
~SequenceCheckerImpl() override = default;
void set_quit_callback(base::OnceClosure callback) {
quit_callback_ = std::move(callback);
}
// mojom::SequenceChecker:
void Bind(
PendingAssociatedReceiver<mojom::SequenceChecker> receiver) override {
receivers_.Add(this, std::move(receiver));
}
void AddClient(
PendingAssociatedRemote<mojom::SequenceCheckerClient> client) override {
clients_.Add(std::move(client));
}
void Check(int32_t n) override {
CHECK_EQ(next_expected_value_, n);
++next_expected_value_;
}
void GetNextExpectedValue(GetNextExpectedValueCallback callback) override {
for (auto& client : clients_)
client->OnNextExpectedValueQueried(next_expected_value_);
std::move(callback).Run(next_expected_value_);
}
void Quit(QuitCallback callback) override {
for (auto& client : clients_)
client->OnQuit();
// Destroys `this`, so we don't bother responding.
DCHECK(quit_callback_);
std::move(quit_callback_).Run();
}
private:
int32_t next_expected_value_ = 0;
AssociatedReceiverSet<mojom::SequenceChecker> receivers_;
AssociatedRemoteSet<mojom::SequenceCheckerClient> clients_;
base::OnceClosure quit_callback_;
};
class SequenceCheckerClientImpl : public mojom::SequenceCheckerClient {
public:
SequenceCheckerClientImpl() = default;
~SequenceCheckerClientImpl() override = default;
PendingAssociatedRemote<mojom::SequenceCheckerClient> MakeRemote() {
PendingAssociatedRemote<mojom::SequenceCheckerClient> remote;
receivers_.Add(this, remote.InitWithNewEndpointAndPassReceiver());
return remote;
}
// mojom::SequenceCheckerClient:
void OnNextExpectedValueQueried(int32_t n) override {}
void OnQuit() override {}
private:
AssociatedReceiverSet<mojom::SequenceCheckerClient> receivers_;
};
TEST_P(RemoteTest, SharedRemotePassAssociatedEndpointsEarly) {
// Verifies that we can start passing associated endpoints over a SharedRemote
// as soon as it's constructed, even if it's still scheduled to bind on a
// background thread.
const scoped_refptr<base::SequencedTaskRunner> other_thread_task_runner =
base::ThreadPool::CreateSequencedTaskRunner({});
PendingRemote<mojom::SequenceChecker> remote;
other_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(
[](PendingReceiver<mojom::SequenceChecker> receiver) {
MakeSelfOwnedReceiver(
std::make_unique<SequenceCheckerImpl>(),
std::move(receiver));
},
remote.InitWithNewPipeAndPassReceiver()));
SharedRemote<mojom::SequenceChecker> checker(std::move(remote),
other_thread_task_runner);
PendingAssociatedRemote<mojom::SequenceChecker> pending_associated_checker;
checker->Bind(
pending_associated_checker.InitWithNewEndpointAndPassReceiver());
SharedAssociatedRemote<mojom::SequenceChecker> associated_checker =
mojo::SharedAssociatedRemote<mojom::SequenceChecker>(
std::move(pending_associated_checker), other_thread_task_runner);
PendingAssociatedRemote<mojom::SequenceChecker>
later_pending_associated_checker;
auto later_associated_receiver =
later_pending_associated_checker.InitWithNewEndpointAndPassReceiver();
SharedAssociatedRemote<mojom::SequenceChecker> later_associated_checker =
mojo::SharedAssociatedRemote<mojom::SequenceChecker>(
std::move(later_pending_associated_checker),
other_thread_task_runner);
checker->Bind(std::move(later_associated_receiver));
checker->Check(0);
associated_checker->Check(1);
later_associated_checker->Check(2);
checker->Check(3);
// Make sure the above Checks reach the impl before we pass the test.
int32_t next_expected_value = 0;
EXPECT_TRUE(checker->GetNextExpectedValue(&next_expected_value));
EXPECT_EQ(4, next_expected_value);
}
TEST_P(RemoteTest, SharedRemoteEarlySyncCall) {
// Verifies that sync calls made immediately after SharedRemote setup (with
// off-thread binding) do not deadlock.
const scoped_refptr<base::SequencedTaskRunner> other_thread_task_runner =
base::ThreadPool::CreateSequencedTaskRunner({});
PendingRemote<mojom::SequenceChecker> remote;
other_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(
[](PendingReceiver<mojom::SequenceChecker> receiver) {
MakeSelfOwnedReceiver(
std::make_unique<SequenceCheckerImpl>(),
std::move(receiver));
},
remote.InitWithNewPipeAndPassReceiver()));
SharedRemote<mojom::SequenceChecker> checker(std::move(remote),
other_thread_task_runner);
int32_t next_expected_value = -1;
EXPECT_TRUE(checker->GetNextExpectedValue(&next_expected_value));
EXPECT_EQ(0, next_expected_value);
}
TEST_P(RemoteTest, SharedRemoteSyncCallWithPendingEventOnSameThread) {
// Verifies that a sync reply on a SharedRemote is properly handled even if
// there's an another event (in this case, an async message to an associated
// interface) ahead of it in the underlying router's task queue.
const scoped_refptr<base::SequencedTaskRunner> other_thread_task_runner =
base::ThreadPool::CreateSequencedTaskRunner({});
PendingRemote<mojom::SequenceChecker> remote;
other_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(
[](PendingReceiver<mojom::SequenceChecker> receiver) {
MakeSelfOwnedReceiver(
std::make_unique<SequenceCheckerImpl>(),
std::move(receiver));
},
remote.InitWithNewPipeAndPassReceiver()));
SharedRemote<mojom::SequenceChecker> checker(std::move(remote),
other_thread_task_runner);
SequenceCheckerClientImpl client;
checker->AddClient(client.MakeRemote());
int32_t next_expected_value = -1;
EXPECT_TRUE(checker->GetNextExpectedValue(&next_expected_value));
EXPECT_EQ(0, next_expected_value);
}
// Flaky on all platforms. https://crbug.com/1224768
TEST_P(RemoteTest, DISABLED_DisconnectDuringOffThreadSyncWaitWithUnprocessedTasks) {
// Regression test for https://crbug.com/1223628.
//
// This tests a fairly obscure edge case where one or more message tasks is
// queued and ready for dispatch to one or more endpoints on a pipe, but
// another endpoint on one of the same sequences is blocking the thread on an
// off-thread sync wait via a SharedAssociatedRemote proxy. We test that in
// this scenario, disconnection of the underlying pipe will interrupt the sync
// wait as expected.
const scoped_refptr<base::SequencedTaskRunner> impl_sequence =
base::ThreadPool::CreateSequencedTaskRunner({});
const scoped_refptr<base::SequencedTaskRunner> associated_sequence =
base::ThreadPool::CreateSequencedTaskRunner({});
SharedRemote<mojom::SequenceChecker> remote;
UniqueReceiverSet<mojom::SequenceChecker> checkers;
base::RunLoop bind_loop;
base::OnceClosure bind_done = bind_loop.QuitClosure();
impl_sequence->PostTask(
FROM_HERE, base::BindLambdaForTesting([&] {
// Make sure the impl clears `checkers` immediately when Quit() is
// invoked so that it self-destructs.
auto impl = std::make_unique<SequenceCheckerImpl>();
impl->set_quit_callback(
base::BindLambdaForTesting([&] { checkers.Clear(); }));
checkers.Add(std::move(impl),
remote.BindNewPipeAndPassReceiver(impl_sequence));
std::move(bind_done).Run();
}));
bind_loop.Run();
// Bind an associated endpoint that sends messages from a different background
// sequence.
SharedAssociatedRemote<mojom::SequenceChecker> associated_remote;
remote->Bind(
associated_remote.BindNewEndpointAndPassReceiver(associated_sequence));
// Add a new client, so that the impl can queue up an outgoing message before
// disconnecting. We do this to ensure there's a non-error task in the local
// endpoint's task queue before disconnection can be observed. The task won't
// dispatch because this thread will be blocked on the sync wait below.
SequenceCheckerClientImpl client;
associated_remote->AddClient(client.MakeRemote());
// Finally, do a sync call. This should still terminate as soon as the remote
// is disconnected by the impl's quit callback set above, despite the fact
// that there will be undispatched tasks queued on the local client endpoint.
// The bug this test is covering would cause this wait to hang indefinitely.
EXPECT_FALSE(associated_remote->Quit());
}
TEST_P(RemoteTest, SharedRemoteDisconnectCallback) {
PendingRemote<math::Calculator> remote;
MathCalculatorImpl calc_impl(remote.InitWithNewPipeAndPassReceiver());
const scoped_refptr<base::SequencedTaskRunner> main_task_runner =
base::ThreadPool::CreateSequencedTaskRunner({});
SharedRemote<math::Calculator> shared_remote(std::move(remote),
main_task_runner);
bool connected = true;
base::RunLoop run_loop;
// Register a callback to set_disconnect_handler. It should be called when the
// pipe is disconnected.
shared_remote.set_disconnect_handler(base::BindLambdaForTesting([&] {
connected = false;
run_loop.Quit();
}),
main_task_runner);
base::RunLoop run_loop2;
shared_remote->Add(123, base::BindLambdaForTesting([&](double result) {
EXPECT_EQ(123, result);
run_loop2.Quit();
}));
run_loop2.Run();
calc_impl.receiver().reset();
run_loop.Run();
// |connected| should be false after calling the disconnect callback.
EXPECT_FALSE(connected);
}
constexpr int32_t kMagicNumber = 42;
class SharedRemoteSyncTestImpl : public mojom::SharedRemoteSyncTest {
public:
SharedRemoteSyncTestImpl() = default;
~SharedRemoteSyncTestImpl() override = default;
// mojom::SharedRemoteSyncTest implementation:
void Fetch(FetchCallback callback) override {
// Post an async task to our current task runner to respond to this message.
// Because the Remote and Receiver are bound to the same sequence, this will
// only run if the Remote doesn't block the sequence on the sync call made
// by the test below.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), kMagicNumber));
}
};
TEST_P(RemoteTest, SharedRemoteSyncOnlyBlocksCallingSequence) {
// Verifies that a sync call on a SharedRemote only blocks the calling
// sequence, not the sequence to which the underlying Remote is bound.
// See https://crbug.com/1016022.
const scoped_refptr<base::SequencedTaskRunner> bound_task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::WithBaseSyncPrimitives()});
PendingRemote<mojom::SharedRemoteSyncTest> pending_remote;
auto receiver = pending_remote.InitWithNewPipeAndPassReceiver();
SharedRemote<mojom::SharedRemoteSyncTest> remote(std::move(pending_remote),
bound_task_runner);
bound_task_runner->PostTask(
FROM_HERE, base::BindOnce(
[](PendingReceiver<mojom::SharedRemoteSyncTest> receiver) {
MakeSelfOwnedReceiver(
std::make_unique<SharedRemoteSyncTestImpl>(),
std::move(receiver));
},
std::move(receiver)));
int32_t value = 0;
remote->Fetch(&value);
EXPECT_EQ(kMagicNumber, value);
remote.reset();
// Resetting |remote| above will ultimately post a task to |bound_task_runner|
// to signal a connection error and trigger the self-owned Receiver's
// destruction. This ensures that the task will run, avoiding leaks.
task_environment()->RunUntilIdle();
}
TEST_P(RemoteTest, SharedRemoteSyncCallsFromOffBoundConstructionSequence) {
// Regression test for https://crbug.com/1102921. Verifies that when
// bound to its construction sequence, a SharedRemote doesn't try blocking
// that sequence when a sync call is made from another sequence.
const scoped_refptr<base::SequencedTaskRunner> background_task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::WithBaseSyncPrimitives()});
// Ensure waiting on the main thread is not allowed so that blocking attempts
// will break the test.
base::DisallowBaseSyncPrimitives();
PendingRemote<mojom::SharedRemoteSyncTest> pending_remote;
SharedRemoteSyncTestImpl impl;
Receiver<mojom::SharedRemoteSyncTest> receiver(
&impl, pending_remote.InitWithNewPipeAndPassReceiver());
int32_t value = 0;
base::RunLoop loop;
base::OnceClosure quit = loop.QuitClosure();
SharedRemote<mojom::SharedRemoteSyncTest> remote(std::move(pending_remote));
background_task_runner->PostTask(
FROM_HERE, base::BindLambdaForTesting([remote, &value, &quit] {
EXPECT_TRUE(remote->Fetch(&value));
EXPECT_EQ(kMagicNumber, value);
std::move(quit).Run();
}));
loop.Run();
// TaskEnvironment teardown wants to block the main thread.
base::internal::ResetThreadRestrictionsForTesting();
}
TEST_P(RemoteTest, SharedRemoteSyncCallsFromBoundNonConstructionSequence) {
// Regression test for https://crbug.com/1102921. Verifies that when
// bound to some sequence other than that which constructed it, a SharedRemote
// properly blocks when making sync calls from the bound sequence.
const scoped_refptr<base::SequencedTaskRunner> background_task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::WithBaseSyncPrimitives()});
PendingRemote<mojom::SharedRemoteSyncTest> pending_remote;
SharedRemoteSyncTestImpl impl;
Receiver<mojom::SharedRemoteSyncTest> receiver(
&impl, pending_remote.InitWithNewPipeAndPassReceiver());
int32_t value = 0;
base::RunLoop loop;
base::OnceClosure quit = loop.QuitClosure();
SharedRemote<mojom::SharedRemoteSyncTest> remote(std::move(pending_remote),
background_task_runner);
background_task_runner->PostTask(
FROM_HERE, base::BindLambdaForTesting([remote, &value, &quit] {
EXPECT_TRUE(remote->Fetch(&value));
EXPECT_EQ(kMagicNumber, value);
std::move(quit).Run();
}));
loop.Run();
}
TEST_P(RemoteTest, RemoteSet) {
std::vector<std::optional<MathCalculatorImpl>> impls(4);
PendingRemote<math::Calculator> remote0;
PendingRemote<math::Calculator> remote1;
PendingRemote<math::Calculator> remote2;
PendingRemote<math::Calculator> remote3;
impls[0].emplace(remote0.InitWithNewPipeAndPassReceiver());
impls[1].emplace(remote1.InitWithNewPipeAndPassReceiver());
impls[2].emplace(remote2.InitWithNewPipeAndPassReceiver());
impls[3].emplace(remote3.InitWithNewPipeAndPassReceiver());
RemoteSet<math::Calculator> remotes;
auto id0 = remotes.Add(Remote<math::Calculator>(std::move(remote0)));
auto id1 = remotes.Add(std::move(remote1));
auto id2 = remotes.Add(std::move(remote2));
auto id3 = remotes.Add(std::move(remote3));
// Send a message to each and wait for a reply.
{
base::RunLoop loop;
constexpr double kValue = 42.0;
auto on_add = base::BarrierClosure(8, loop.QuitClosure());
for (auto& remote : remotes) {
remote->Add(kValue, base::BindLambdaForTesting([&](double total) {
EXPECT_EQ(kValue, total);
on_add.Run();
}));
}
// Use Get() to get a specified remote from RemoteSet.
std::vector<mojo::RemoteSetElementId> ids = {id0, id1, id2, id3};
for (auto& id : ids) {
remotes.Get(id)->Add(kValue,
base::BindLambdaForTesting([&](double total) {
EXPECT_EQ(kValue * 2, total);
on_add.Run();
}));
}
loop.Run();
EXPECT_EQ(kValue * 2, impls[0]->total());
EXPECT_EQ(kValue * 2, impls[1]->total());
EXPECT_EQ(kValue * 2, impls[2]->total());
EXPECT_EQ(kValue * 2, impls[3]->total());
}
EXPECT_FALSE(remotes.empty());
// Wipe out each of the impls and wait for a disconnect notification for each.
{
base::RunLoop loop;
remotes.set_disconnect_handler(
base::BindLambdaForTesting([&](RemoteSetElementId id) {
EXPECT_EQ(id, id0);
EXPECT_FALSE(remotes.Contains(id0));
EXPECT_TRUE(remotes.Contains(id1));
EXPECT_TRUE(remotes.Contains(id2));
EXPECT_TRUE(remotes.Contains(id3));
loop.Quit();
}));
impls[0].reset();
loop.Run();
}
EXPECT_FALSE(remotes.empty());
{
base::RunLoop loop;
remotes.set_disconnect_handler(
base::BindLambdaForTesting([&](RemoteSetElementId id) {
EXPECT_EQ(id, id2);
EXPECT_FALSE(remotes.Contains(id0));
EXPECT_TRUE(remotes.Contains(id1));
EXPECT_FALSE(remotes.Contains(id2));
EXPECT_TRUE(remotes.Contains(id3));
loop.Quit();
}));
impls[2].reset();
loop.Run();
}
EXPECT_FALSE(remotes.empty());
{
// Test that remote set disconnect_with_reason_handler can handle resets
// without reason.
base::RunLoop loop;
remotes.set_disconnect_with_reason_handler(base::BindLambdaForTesting(
[&](RemoteSetElementId id, uint32_t custom_reason_code,
const std::string& description) {
EXPECT_EQ(id, id1);
EXPECT_EQ(custom_reason_code, static_cast<uint32_t>(0));
EXPECT_EQ(description, "");
EXPECT_FALSE(remotes.Contains(id0));
EXPECT_FALSE(remotes.Contains(id1));
EXPECT_FALSE(remotes.Contains(id2));
EXPECT_TRUE(remotes.Contains(id3));
loop.Quit();
}));
impls[1].reset();
loop.Run();
}
EXPECT_FALSE(remotes.empty());
{
// Test that remote set disconnect_with_reason_handler can handle resets
// with reason.
base::RunLoop loop;
remotes.set_disconnect_with_reason_handler(base::BindLambdaForTesting(
[&](RemoteSetElementId id, uint32_t custom_reason_code,
const std::string& description) {
EXPECT_EQ(id, id3);
EXPECT_EQ(custom_reason_code, static_cast<uint32_t>(10));
EXPECT_EQ(description, "custom description");
EXPECT_FALSE(remotes.Contains(id0));
EXPECT_FALSE(remotes.Contains(id1));
EXPECT_FALSE(remotes.Contains(id2));
EXPECT_FALSE(remotes.Contains(id3));
loop.Quit();
}));
impls[3]->receiver().ResetWithReason(10, "custom description");
loop.Run();
}
EXPECT_TRUE(remotes.empty());
}
bool* dump_without_crashing_flag;
extern "C" void HandleDumpWithoutCrashing() {
*dump_without_crashing_flag = true;
}
class LargeMessageTestImpl : public mojom::LargeMessageTest {
public:
explicit LargeMessageTestImpl(
PendingReceiver<mojom::LargeMessageTest> receiver)
: receiver_(this, std::move(receiver)) {}
~LargeMessageTestImpl() override = default;
// mojom::LargeMessageTest implementation:
void ProcessData(const std::vector<uint8_t>& data,
ProcessDataCallback callback) override {
std::move(callback).Run(data.size());
}
void ProcessLotsOfData(const std::vector<uint8_t>& data,
ProcessLotsOfDataCallback callback) override {
std::move(callback).Run(data.size());
}
void GetLotsOfData(uint64_t data_size,
GetLotsOfDataCallback callback) override {
std::move(callback).Run(std::vector<uint8_t>(data_size));
}
private:
Receiver<mojom::LargeMessageTest> receiver_;
};
// TODO(crbug.com/40226674): Flaky on Linux/ASAN, Mac, and Fuchsia bots.
TEST_P(RemoteTest, DISABLED_SendVeryLargeMessages) {
Remote<mojom::LargeMessageTest> remote;
LargeMessageTestImpl impl(remote.BindNewPipeAndPassReceiver());
bool did_dump_without_crashing = false;
dump_without_crashing_flag = &did_dump_without_crashing;
base::debug::SetDumpWithoutCrashingFunction(&HandleDumpWithoutCrashing);
// The test runner configures Mojo to cap message size at
// `kMaxMessageSizeInTests`, so we test with data that's double that size.
constexpr size_t kBigDataSize =
core::test::MojoTestBase::kMaxMessageSizeInTests * 2;
std::vector<uint8_t> lots_of_data(kBigDataSize);
uint64_t data_size = 0;
ASSERT_TRUE(remote->ProcessData(lots_of_data, &data_size));
EXPECT_EQ(kBigDataSize, data_size);
if (GetParam() == BindingsTestSerializationMode::kNeverSerialize) {
// If the message is never serialized, there won't be a crash report even
// without the [UnlimitedSize] attribute.
EXPECT_FALSE(did_dump_without_crashing);
} else {
EXPECT_TRUE(did_dump_without_crashing);
}
did_dump_without_crashing = false;
data_size = 0;
ASSERT_TRUE(remote->ProcessLotsOfData(lots_of_data, &data_size));
EXPECT_EQ(kBigDataSize, data_size);
// Serialized or not, this message won't generate a crash report because it's
// explicitly marked with [UnlimitedSize].
EXPECT_FALSE(did_dump_without_crashing);
// [UnlimitedSize] also allows replies to be large.
did_dump_without_crashing = false;
lots_of_data.clear();
ASSERT_TRUE(remote->GetLotsOfData(kBigDataSize, &lots_of_data));
EXPECT_EQ(kBigDataSize, lots_of_data.size());
EXPECT_FALSE(did_dump_without_crashing);
base::debug::SetDumpWithoutCrashingFunction(nullptr);
}
INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(RemoteTest);
INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(EndToEndRemoteTest);
} // namespace
} // namespace remote_unittest
} // namespace test
} // namespace mojo