blob: 0aed79adbdf35f60c0fc1727e3303957b9f209cd [file] [log] [blame]
// Copyright 2023 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/tests/feature_unittest.test-mojom-features.h"
#include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom-forward.h"
#include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom-shared.h"
#include "mojo/public/cpp/bindings/tests/feature_unittest.test-mojom.h"
#include <utility>
#include "base/dcheck_is_on.h"
#include "base/feature_list.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/bindings/service_factory.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/system/functions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo::test::feature_unittest {
namespace {
//// Mojom impls ////
class DefaultDeniedImpl : public mojom::DefaultDenied {
public:
explicit DefaultDeniedImpl(
mojo::PendingReceiver<mojom::DefaultDenied> receiver)
: receiver_(this, std::move(receiver)) {}
DefaultDeniedImpl() : receiver_(this) {}
~DefaultDeniedImpl() override = default;
DefaultDeniedImpl(const DefaultDeniedImpl&) = delete;
DefaultDeniedImpl& operator=(const DefaultDeniedImpl&) = delete;
// mojom::DefaultDenied overrides.
void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); }
mojo::Receiver<mojom::DefaultDenied>& receiver() { return receiver_; }
// Helper for ServiceFactory test.
static auto RunService(PendingReceiver<mojom::DefaultDenied> receiver) {
return std::make_unique<DefaultDeniedImpl>(std::move(receiver));
}
private:
mojo::Receiver<mojom::DefaultDenied> receiver_;
};
class OtherDeniedImpl : public mojom::OtherDenied {
public:
explicit OtherDeniedImpl(mojo::PendingReceiver<mojom::OtherDenied> receiver)
: receiver_(this, std::move(receiver)) {}
OtherDeniedImpl() : receiver_(this) {}
~OtherDeniedImpl() override = default;
OtherDeniedImpl(const OtherDeniedImpl&) = delete;
OtherDeniedImpl& operator=(const OtherDeniedImpl&) = delete;
// mojom::OtherDenied overrides.
void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); }
mojo::Receiver<mojom::OtherDenied>& receiver() { return receiver_; }
private:
mojo::Receiver<mojom::OtherDenied> receiver_;
};
class OtherAllowedImpl : public mojom::OtherAllowed {
public:
explicit OtherAllowedImpl(mojo::PendingReceiver<mojom::OtherAllowed> receiver)
: receiver_(this, std::move(receiver)) {}
OtherAllowedImpl() : receiver_(this) {}
~OtherAllowedImpl() override = default;
OtherAllowedImpl(const OtherAllowedImpl&) = delete;
OtherAllowedImpl& operator=(const OtherAllowedImpl&) = delete;
// mojom::OtherAllowed overrides.
void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); }
mojo::Receiver<mojom::OtherAllowed>& receiver() { return receiver_; }
private:
mojo::Receiver<mojom::OtherAllowed> receiver_;
};
class DefaultDeniedSelfOwnedImpl : public mojom::DefaultDenied {
public:
DefaultDeniedSelfOwnedImpl() = default;
~DefaultDeniedSelfOwnedImpl() override = default;
DefaultDeniedSelfOwnedImpl(const DefaultDeniedSelfOwnedImpl&) = delete;
DefaultDeniedSelfOwnedImpl& operator=(const DefaultDeniedSelfOwnedImpl&) =
delete;
// mojom::DefaultDenied overrides.
void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); }
};
class DefaultAllowedSelfOwnedImpl : public mojom::DefaultAllowed {
public:
DefaultAllowedSelfOwnedImpl() = default;
~DefaultAllowedSelfOwnedImpl() override = default;
DefaultAllowedSelfOwnedImpl(const DefaultAllowedSelfOwnedImpl&) = delete;
DefaultAllowedSelfOwnedImpl& operator=(const DefaultAllowedSelfOwnedImpl&) =
delete;
// mojom::DefaultAllowed overrides.
void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); }
};
class DefaultAllowedImpl : mojom::DefaultAllowed {
public:
explicit DefaultAllowedImpl(
mojo::PendingReceiver<mojom::DefaultAllowed> receiver)
: receiver_(this, std::move(receiver)) {}
~DefaultAllowedImpl() override = default;
DefaultAllowedImpl(const DefaultAllowedImpl&) = delete;
DefaultAllowedImpl& operator=(const DefaultAllowedImpl&) = delete;
// mojom::DefaultAllowed overrides.
void GetInt(GetIntCallback callback) override { std::move(callback).Run(1); }
mojo::Receiver<mojom::DefaultAllowed>& receiver() { return receiver_; }
// Helper for ServiceFactory test.
static auto RunService(PendingReceiver<mojom::DefaultAllowed> receiver) {
return std::make_unique<DefaultAllowedImpl>(std::move(receiver));
}
private:
mojo::Receiver<mojom::DefaultAllowed> receiver_;
};
class FeaturesOnMethodsImpl : public mojom::FeaturesOnMethods {
public:
explicit FeaturesOnMethodsImpl(
mojo::PendingReceiver<mojom::FeaturesOnMethods> receiver)
: receiver_(this, std::move(receiver)), assoc_receiver_(nullptr) {}
explicit FeaturesOnMethodsImpl(
mojo::PendingAssociatedReceiver<mojom::FeaturesOnMethods> receiver)
: receiver_(nullptr), assoc_receiver_(this, std::move(receiver)) {}
~FeaturesOnMethodsImpl() override = default;
FeaturesOnMethodsImpl(const FeaturesOnMethodsImpl&) = delete;
FeaturesOnMethodsImpl& operator=(const FeaturesOnMethodsImpl&) = delete;
// mojom::FeaturesOnMethods overrides.
void DefaultDenied(DefaultDeniedCallback callback) override {
std::move(callback).Run(1);
}
void DefaultDeniedSync(DefaultDeniedCallback callback) override {
std::move(callback).Run(1);
}
void DefaultAllowed(DefaultAllowedCallback callback) override {
std::move(callback).Run(1);
}
void Normal(NormalCallback callback) override { std::move(callback).Run(1); }
private:
mojo::Receiver<mojom::FeaturesOnMethods> receiver_;
mojo::AssociatedReceiver<mojom::FeaturesOnMethods> assoc_receiver_;
};
class PassesInterfacesImpl : public mojom::PassesInterfaces {
public:
explicit PassesInterfacesImpl(
mojo::PendingReceiver<mojom::PassesInterfaces> receiver)
: receiver_(this, std::move(receiver)) {}
~PassesInterfacesImpl() override = default;
PassesInterfacesImpl(const PassesInterfacesImpl&) = delete;
PassesInterfacesImpl& operator=(const PassesInterfacesImpl&) = delete;
// Callback to service responses from a mojom::DefaultAllowed remote.
void OnGetInt(PassPendingOptionalRemoteAllowedCallback callback, int val) {
std::move(callback).Run(val == 1);
}
// mojom::PassesInterfaces overrides.
void PassPendingOptionalRemoteAllowed(
mojo::PendingRemote<mojom::DefaultAllowed> iface,
PassPendingOptionalRemoteAllowedCallback callback) override {
allowed_remote_.Bind(std::move(iface));
ASSERT_TRUE(allowed_remote_);
allowed_remote_->GetInt(base::BindOnce(&PassesInterfacesImpl::OnGetInt,
base::Unretained(this),
std::move(callback)));
}
void PassPendingOptionalRemoteDisabled(
mojo::PendingRemote<mojom::DefaultDenied> iface,
PassPendingOptionalRemoteDisabledCallback callback) override {
// As disabled interfaces are serialized as if an unbound pipe was
// passed this method is called but it does not receive a pipe to bind.
ASSERT_FALSE(iface.is_valid());
std::move(callback).Run(true);
}
void PassPendingOptionalReceiverAllowed(
mojo::PendingReceiver<mojom::DefaultAllowed> iface) override {
allowed_impl_ = std::make_unique<DefaultAllowedImpl>(std::move(iface));
ASSERT_TRUE(allowed_impl_->receiver().is_bound());
}
void PassPendingOptionalReceiverDisabled(
mojo::PendingReceiver<mojom::DefaultDenied> iface,
PassPendingOptionalReceiverDisabledCallback callback) override {
denied_impl_ = std::make_unique<DefaultDeniedImpl>(std::move(iface));
ASSERT_FALSE(denied_impl_->receiver().is_bound());
std::move(callback).Run(true);
}
private:
mojo::Receiver<mojom::PassesInterfaces> receiver_;
mojo::Remote<mojom::DefaultAllowed> allowed_remote_;
mojo::Remote<mojom::DefaultDenied> denied_remote_;
std::unique_ptr<DefaultAllowedImpl> allowed_impl_;
std::unique_ptr<DefaultDeniedImpl> denied_impl_;
};
} // namespace
//// Test runners ////
class FeatureBindingsTest : public BindingsTestBase {
public:
void SetUp() override {
mojo::SetDefaultProcessErrorHandler(base::BindRepeating(
&FeatureBindingsTest::OnProcessError, base::Unretained(this)));
}
void TearDown() override {
mojo::SetDefaultProcessErrorHandler(base::NullCallback());
}
void SetErrorHandler(base::OnceClosure handler) {
error_handler_ = std::move(handler);
}
private:
void OnProcessError(const std::string& error) {
if (error_handler_) {
std::move(error_handler_).Run();
}
}
base::OnceClosure error_handler_;
};
//// Tests /////
TEST(FeatureTest, FeatureBasics) {
EXPECT_TRUE(base::FeatureList::IsEnabled(
mojo::test::feature_unittest::mojom::TestFeatureOn));
EXPECT_FALSE(base::FeatureList::IsEnabled(
mojo::test::feature_unittest::mojom::TestFeatureOff));
}
TEST(FeatureTest, ScopedFeatures) {
base::test::ScopedFeatureList feature_list1;
// --enable-features=TestFeatureOff --disable-features=TestFeatureOn.
feature_list1.InitFromCommandLine("TestFeatureOff", "TestFeatureOn");
// Check state is affected by the command line.
EXPECT_FALSE(base::FeatureList::IsEnabled(
mojo::test::feature_unittest::mojom::TestFeatureOn));
EXPECT_TRUE(base::FeatureList::IsEnabled(
mojo::test::feature_unittest::mojom::TestFeatureOff));
}
//// Tests that enabled features allow normal behavior. ////
TEST_P(FeatureBindingsTest, DefaultAllowed) {
// Validate that allowed interfaces on `DefaultAllowed` can be called.
bool error = false;
SetErrorHandler(base::BindLambdaForTesting([&] { error = true; }));
Remote<mojom::DefaultAllowed> remote;
DefaultAllowedImpl impl(remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(remote);
base::RunLoop run_loop;
remote->GetInt(base::BindLambdaForTesting([&](int) { run_loop.Quit(); }));
run_loop.Run();
EXPECT_FALSE(error);
}
TEST_P(FeatureBindingsTest, OtherAllowed) {
// Validate that allowed interfaces on `OtherAllowed` can be called. This
// interface imports its features from another file.
bool error = false;
SetErrorHandler(base::BindLambdaForTesting([&] { error = true; }));
Remote<mojom::OtherAllowed> remote;
OtherAllowedImpl impl(remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(remote);
base::RunLoop run_loop;
remote->GetInt(base::BindLambdaForTesting([&](int) { run_loop.Quit(); }));
run_loop.Run();
EXPECT_FALSE(error);
}
TEST_P(FeatureBindingsTest, FeaturesOnMethodsAllowed) {
// Validate that allowed interfaces on `FeaturesOnMethods` can be called.
bool error = false;
SetErrorHandler(base::BindLambdaForTesting([&] { error = true; }));
Remote<mojom::FeaturesOnMethods> remote;
EXPECT_FALSE(remote);
FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(remote);
base::RunLoop run_loop;
remote->DefaultAllowed(
base::BindLambdaForTesting([&](int) { run_loop.Quit(); }));
run_loop.Run();
base::RunLoop run_loop2;
remote->Normal(base::BindLambdaForTesting([&](int) { run_loop2.Quit(); }));
run_loop2.Run();
EXPECT_FALSE(error);
}
TEST_P(FeatureBindingsTest, FeaturesOnReceiverDenied) {
if (GetParam() == mojo::BindingsTestSerializationMode::kNeverSerialize) {
return;
}
// Validate that disabled methods on `FeaturesOnMethods` cannot be called.
bool called = false;
bool called_disconnect = false;
base::RunLoop run_loop;
Remote<mojom::FeaturesOnMethods> remote;
auto pending_receiver = remote.BindNewPipeAndPassReceiver();
remote.set_disconnect_handler(base::BindLambdaForTesting([&] {
called_disconnect = true;
run_loop.Quit();
}));
{
// Queue to the pending_receiver with the feature enabled.
// --enable-features=TestFeatureOff.
base::test::ScopedFeatureList feature_list;
feature_list.InitFromCommandLine("TestFeatureOff", "");
remote->DefaultDenied(
base::BindLambdaForTesting([&](int) { called = true; }));
}
// Receiver can now process messages and TestFeatureOff is now disabled.
FeaturesOnMethodsImpl impl(std::move(pending_receiver));
run_loop.Run();
EXPECT_FALSE(called);
EXPECT_TRUE(called_disconnect);
}
TEST_P(FeatureBindingsTest, PassesInterfacesAllowed) {
// Validate that feature-protected interfaces can be passed over mojo methods.
bool error = false;
SetErrorHandler(base::BindLambdaForTesting([&] { error = true; }));
Remote<mojom::PassesInterfaces> remote;
EXPECT_FALSE(remote);
PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(remote);
{
// Can call methods via a passed allowed pending remote.
base::RunLoop run_loop;
MessagePipe pipe;
PendingRemote<mojom::DefaultAllowed> allowed_pending_remote(
std::move(pipe.handle0), 0);
PendingReceiver<mojom::DefaultAllowed> allowed_pending_receiver(
std::move(pipe.handle1));
bool cb_val = false;
remote->PassPendingOptionalRemoteAllowed(
std::move(allowed_pending_remote),
base::BindLambdaForTesting([&](bool val) {
cb_val = val;
run_loop.Quit();
}));
DefaultAllowedImpl allowed_impl(std::move(allowed_pending_receiver));
run_loop.Run();
ASSERT_TRUE(cb_val);
}
{
// Can call methods via a passed allowed pending receiver.
base::RunLoop run_loop;
MessagePipe pipe;
PendingRemote<mojom::DefaultAllowed> allowed_pending_remote(
std::move(pipe.handle0), 0);
PendingReceiver<mojom::DefaultAllowed> allowed_pending_receiver(
std::move(pipe.handle1));
bool cb_val = false;
remote->PassPendingOptionalReceiverAllowed(
std::move(allowed_pending_receiver));
Remote<mojom::DefaultAllowed> allowed(std::move(allowed_pending_remote));
allowed->GetInt(base::BindLambdaForTesting([&](int val) {
cb_val = val == 1;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_TRUE(cb_val);
}
}
TEST_P(FeatureBindingsTest, SetsAllowed) {
// Features intervene at the set.Add() stage so validate that these work.
Remote<mojom::DefaultAllowed> remote_a;
Remote<mojom::DefaultAllowed> remote_b;
DefaultAllowedImpl impl_a(remote_a.BindNewPipeAndPassReceiver());
DefaultAllowedImpl impl_b(remote_b.BindNewPipeAndPassReceiver());
PendingRemote<mojom::DefaultAllowed> pending_remote_c;
auto pending_receiver_c = pending_remote_c.InitWithNewPipeAndPassReceiver();
PendingRemote<mojom::DefaultAllowed> pending_remote_d;
auto pending_receiver_d = pending_remote_d.InitWithNewPipeAndPassReceiver();
auto impl_c = std::make_unique<DefaultAllowedSelfOwnedImpl>();
auto impl_d = std::make_unique<DefaultAllowedSelfOwnedImpl>();
ReceiverSet<mojom::DefaultAllowed> receiver_set;
// Every .Add() variant routes to AddImpl in an internal class.
receiver_set.Add(impl_c.get(), std::move(pending_receiver_c));
receiver_set.Add(impl_d.get(), std::move(pending_receiver_d));
RemoteSet<mojom::DefaultAllowed> remote_set;
remote_set.Add(std::move(remote_a));
remote_set.Add(std::move(remote_b));
remote_set.Add(std::move(pending_remote_c));
remote_set.Add(std::move(pending_remote_d));
}
TEST_P(FeatureBindingsTest, ServiceFactory) {
// Service factory quietly skips Adding disabled helpers.
ServiceFactory services;
MessagePipe pipe_allowed;
MessagePipe pipe_denied;
services.Add(DefaultAllowedImpl::RunService);
services.Add(DefaultDeniedImpl::RunService);
GenericPendingReceiver gpr_allowed(mojom::DefaultAllowed::Name_,
std::move(pipe_allowed.handle0));
GenericPendingReceiver gpr_denied(mojom::DefaultDenied::Name_,
std::move(pipe_denied.handle0));
EXPECT_TRUE(services.CanRunService(gpr_allowed));
EXPECT_FALSE(services.CanRunService(gpr_denied));
}
////
// Test that disabled features prevent binding or sending messages.
//
// These tests DCHECK pretty early (in the same helper that inspects feature
// state) so we cannot meaningfully run them in DCHECK builds.
////
#if !DCHECK_IS_ON()
//// mojo::(Associated)?Remote<> ////
TEST_P(FeatureBindingsTest, InertPendingRemoteWontInit) {
// Inert pipes still cannot be initialized.
PendingRemote<mojom::DefaultDenied> pending_remote;
EXPECT_FALSE(pending_remote);
PendingReceiver<mojom::DefaultDenied> pending_receiver =
pending_remote.InitWithNewPipeAndPassReceiver();
EXPECT_FALSE(pending_receiver);
EXPECT_FALSE(pending_remote);
}
TEST_P(FeatureBindingsTest, InertPendingReceiverWontInit) {
PendingReceiver<mojom::DefaultDenied> pending_receiver;
EXPECT_FALSE(pending_receiver);
PendingRemote<mojom::DefaultDenied> pending_remote =
pending_receiver.InitWithNewPipeAndPassRemote();
EXPECT_FALSE(pending_receiver);
EXPECT_FALSE(pending_remote);
}
TEST_P(FeatureBindingsTest, InertPendingAssociatedRemoteWontInit) {
// Inert pipes still cannot be initialized.
PendingAssociatedRemote<mojom::DefaultDenied> pending_remote;
EXPECT_FALSE(pending_remote);
PendingAssociatedReceiver<mojom::DefaultDenied> pending_receiver =
pending_remote.InitWithNewEndpointAndPassReceiver();
EXPECT_FALSE(pending_receiver);
EXPECT_FALSE(pending_remote);
}
TEST_P(FeatureBindingsTest, InertPendingAssociatedReceiverWontInit) {
PendingAssociatedReceiver<mojom::DefaultDenied> pending_receiver;
EXPECT_FALSE(pending_receiver);
PendingAssociatedRemote<mojom::DefaultDenied> pending_remote =
pending_receiver.InitWithNewEndpointAndPassRemote();
EXPECT_FALSE(pending_receiver);
EXPECT_FALSE(pending_remote);
}
TEST_P(FeatureBindingsTest, RemoteWontBindNewPipe) {
Remote<mojom::DefaultDenied> remote;
EXPECT_FALSE(remote);
PendingReceiver<mojom::DefaultDenied> pending_receiver =
remote.BindNewPipeAndPassReceiver();
EXPECT_FALSE(pending_receiver);
}
TEST_P(FeatureBindingsTest, RemoteWontBindPendingRemote) {
MessagePipe pipe;
PendingRemote<mojom::DefaultDenied> pending_remote(std::move(pipe.handle0),
0);
Remote<mojom::DefaultDenied> remote;
EXPECT_FALSE(remote);
remote.Bind(std::move(pending_remote));
EXPECT_FALSE(remote);
}
TEST_P(FeatureBindingsTest, AssociatedRemoteWontBind) {
PendingAssociatedRemote<mojom::DefaultDenied> pa_remote;
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver;
{
base::test::ScopedFeatureList feature_list1;
// --enable-features=TestFeatureOff.
feature_list1.InitFromCommandLine("TestFeatureOff", "");
pa_receiver = pa_remote.InitWithNewEndpointAndPassReceiver();
}
EXPECT_TRUE(pa_remote);
AssociatedRemote<mojom::DefaultDenied> a_remote;
a_remote.Bind(std::move(pa_remote));
EXPECT_FALSE(a_remote);
}
TEST_P(FeatureBindingsTest, AssociatedRemoteWontBindNewEndpoint) {
AssociatedRemote<mojom::DefaultDenied> a_remote;
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver =
a_remote.BindNewEndpointAndPassReceiver();
EXPECT_FALSE(a_remote);
EXPECT_FALSE(pa_receiver);
}
TEST_P(FeatureBindingsTest, AssociatedRemoteWontBindDedicated) {
AssociatedRemote<mojom::DefaultDenied> a_remote;
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver =
a_remote.BindNewEndpointAndPassDedicatedReceiver();
EXPECT_FALSE(a_remote);
EXPECT_FALSE(pa_receiver);
}
//// mojo::(Associated)?Receiver<> ////
TEST_P(FeatureBindingsTest, ReceiverWontBindNewPipe) {
DefaultDeniedImpl impl;
PendingRemote<mojom::DefaultDenied> pending_remote =
impl.receiver().BindNewPipeAndPassRemote();
EXPECT_FALSE(pending_remote);
}
TEST_P(FeatureBindingsTest, ReceiverWontBindPendingReceiver) {
MessagePipe pipe;
PendingReceiver<mojom::DefaultDenied> pending_receiver(
std::move(pipe.handle0));
EXPECT_TRUE(pending_receiver);
DefaultDeniedImpl impl(std::move(pending_receiver));
EXPECT_FALSE(impl.receiver().is_bound());
}
TEST_P(FeatureBindingsTest, AssociatedReceiverWontBind) {
PendingAssociatedRemote<mojom::DefaultDenied> pa_remote;
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver =
pa_remote.InitWithNewEndpointAndPassReceiver();
DefaultDeniedSelfOwnedImpl impl;
AssociatedReceiver<mojom::DefaultDenied> a_receiver(&impl);
a_receiver.Bind(std::move(pa_receiver), nullptr);
EXPECT_FALSE(a_receiver.is_bound());
}
TEST_P(FeatureBindingsTest, AssociatedReceiverWontBindNewEndpoint) {
DefaultDeniedSelfOwnedImpl impl;
AssociatedReceiver<mojom::DefaultDenied> a_receiver(&impl);
auto a_remote = a_receiver.BindNewEndpointAndPassRemote(nullptr);
EXPECT_FALSE(a_receiver);
EXPECT_FALSE(a_remote);
}
TEST_P(FeatureBindingsTest, AssociatedReceiverWontBindDedicated) {
DefaultDeniedSelfOwnedImpl impl;
AssociatedReceiver<mojom::DefaultDenied> a_receiver(&impl);
auto a_remote = a_receiver.BindNewEndpointAndPassDedicatedRemote();
EXPECT_FALSE(a_receiver);
EXPECT_FALSE(a_remote);
}
////
// mojo::Selfowned(Associated)?Receiver bail out without binding the underlying
// receivers.
////
TEST_P(FeatureBindingsTest, SelfOwnedReceiver) {
MessagePipe pipe;
PendingReceiver<mojom::DefaultDenied> pending_receiver(
std::move(pipe.handle0));
auto impl = std::make_unique<DefaultDeniedSelfOwnedImpl>();
auto weak_ref =
MakeSelfOwnedReceiver(std::move(impl), std::move(pending_receiver));
EXPECT_FALSE(weak_ref);
}
TEST_P(FeatureBindingsTest, SelfOwnedAssociatedReceiver) {
PendingAssociatedRemote<mojom::DefaultDenied> pa_remote;
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver =
pa_remote.InitWithNewEndpointAndPassReceiver();
auto impl = std::make_unique<DefaultDeniedSelfOwnedImpl>();
auto weak_ref =
MakeSelfOwnedAssociatedReceiver(std::move(impl), std::move(pa_receiver));
EXPECT_FALSE(weak_ref);
}
////
// mojo::Shared* bail out appropriately.
////
TEST_P(FeatureBindingsTest, SharedRemoteWontBindNewPipe) {
SharedRemote<mojom::DefaultDenied> s_remote;
EXPECT_FALSE(s_remote);
PendingReceiver<mojom::DefaultDenied> p_receiver =
s_remote.BindNewPipeAndPassReceiver();
EXPECT_FALSE(s_remote);
EXPECT_FALSE(p_receiver);
}
TEST_P(FeatureBindingsTest, SharedRemoteWontBind) {
PendingRemote<mojom::DefaultDenied> pa_remote;
PendingReceiver<mojom::DefaultDenied> pa_receiver;
{
base::test::ScopedFeatureList feature_list1;
// --enable-features=TestFeatureOff.
feature_list1.InitFromCommandLine("TestFeatureOff", "");
pa_receiver = pa_remote.InitWithNewPipeAndPassReceiver();
}
EXPECT_TRUE(pa_remote);
SharedRemote<mojom::DefaultDenied> s_remote;
s_remote.Bind(std::move(pa_remote), nullptr);
EXPECT_FALSE(s_remote);
}
TEST_P(FeatureBindingsTest, SharedAssociatedRemoteWontBindNewEndpoint) {
SharedAssociatedRemote<mojom::DefaultDenied> sa_remote;
EXPECT_FALSE(sa_remote);
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver =
sa_remote.BindNewEndpointAndPassReceiver();
EXPECT_FALSE(sa_remote);
EXPECT_FALSE(pa_receiver);
}
TEST_P(FeatureBindingsTest, SharedAssociatedRemoteWontBind) {
PendingAssociatedRemote<mojom::DefaultDenied> pa_remote;
PendingAssociatedReceiver<mojom::DefaultDenied> pa_receiver;
{
base::test::ScopedFeatureList feature_list1;
// --enable-features=TestFeatureOff.
feature_list1.InitFromCommandLine("TestFeatureOff", "");
pa_receiver = pa_remote.InitWithNewEndpointAndPassReceiver();
}
EXPECT_TRUE(pa_remote);
SharedAssociatedRemote<mojom::DefaultDenied> sa_remote;
sa_remote.Bind(std::move(pa_remote), nullptr);
EXPECT_FALSE(sa_remote);
}
////
// Generic*Receiver As<disabled> won't "cast" but allowed interfaces can.
////
TEST_P(FeatureBindingsTest, GenericPendingReceivers) {
MessagePipe pipe_a;
ScopedInterfaceEndpointHandle end_a;
ScopedInterfaceEndpointHandle end_b;
ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&end_a, &end_b);
GenericPendingReceiver gpr_allowed(mojom::DefaultAllowed::Name_,
std::move(pipe_a.handle0));
GenericPendingReceiver gpr_denied(mojom::DefaultDenied::Name_,
std::move(pipe_a.handle1));
GenericPendingAssociatedReceiver gpar_allowed(mojom::DefaultAllowed::Name_,
std::move(end_a));
GenericPendingAssociatedReceiver gpar_denied(mojom::DefaultDenied::Name_,
std::move(end_b));
auto p_allowed = gpr_allowed.As<mojom::DefaultAllowed>();
auto p_denied = gpr_denied.As<mojom::DefaultDenied>();
auto pa_allowed = gpar_allowed.As<mojom::DefaultAllowed>();
auto pa_denied = gpar_denied.As<mojom::DefaultDenied>();
EXPECT_TRUE(p_allowed);
EXPECT_TRUE(pa_allowed);
EXPECT_FALSE(p_denied);
EXPECT_FALSE(pa_denied);
}
////
// Test that pending_* cannot be transmitted by methods. This prevents a
// process from receiving an endpoint from a compromised process. Optional
// remotes and receivers are treated as being empty.
////
TEST_P(FeatureBindingsTest, PassesOptionalInterfacesRemoteDenied) {
if (GetParam() == BindingsTestSerializationMode::kNeverSerialize) {
return;
}
Remote<mojom::PassesInterfaces> remote;
PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
MessagePipe pipe;
PendingRemote<mojom::DefaultDenied> denied_pending_remote(
std::move(pipe.handle0), 0);
bool error = false;
SetErrorHandler(base::BindLambdaForTesting([&] {
error = true;
run_loop.Quit();
}));
bool called_empty = false;
remote->PassPendingOptionalRemoteDisabled(
std::move(denied_pending_remote),
base::BindLambdaForTesting([&](bool val) {
called_empty = val;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_FALSE(error);
ASSERT_TRUE(called_empty);
}
TEST_P(FeatureBindingsTest, PassesOptionalInterfacesReceiverDenied) {
if (GetParam() == mojo::BindingsTestSerializationMode::kNeverSerialize) {
return;
}
Remote<mojom::PassesInterfaces> remote;
PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
MessagePipe pipe;
PendingReceiver<mojom::DefaultDenied> denied_pending_receiver(
std::move(pipe.handle1));
bool error = false;
SetErrorHandler(base::BindLambdaForTesting([&] {
error = true;
run_loop.Quit();
}));
bool called_empty = false;
remote->PassPendingOptionalReceiverDisabled(
std::move(denied_pending_receiver),
base::BindLambdaForTesting([&](bool val) {
called_empty = val;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_FALSE(error);
ASSERT_TRUE(called_empty);
}
////
// Tests that features from a different mojom file work (not exhaustive) - as
// features are implemented by templates this demonstrates that the generated
// headers are correctly imported in the main interface's files.
////
TEST_P(FeatureBindingsTest, RemoteWontBindNewPipeImported) {
// Interface is runtime disabled - so cannot bind.
Remote<mojom::OtherDenied> remote;
PendingReceiver<mojom::OtherDenied> pending_receiver =
remote.BindNewPipeAndPassReceiver();
EXPECT_FALSE(pending_receiver);
}
TEST_P(FeatureBindingsTest, ReceiverWontBindPendingReceiverImported) {
// Interface is runtime disabled - so cannot bind.
MessagePipe pipe;
PendingReceiver<mojom::OtherDenied> pending_receiver(std::move(pipe.handle0));
OtherDeniedImpl impl(std::move(pending_receiver));
EXPECT_FALSE(impl.receiver().is_bound());
}
// Validate that the various Sets return nullopt if a disabled interface is
// provided.
TEST_P(FeatureBindingsTest, ReceiverSetDenied) {
MessagePipe pipe_a;
PendingReceiver<mojom::DefaultDenied> pending_receiver_a(
std::move(pipe_a.handle0));
auto impl = std::make_unique<DefaultDeniedSelfOwnedImpl>();
ReceiverSet<mojom::DefaultDenied> receiver_set;
EXPECT_EQ(receiver_set.Add(impl.get(), std::move(pending_receiver_a)),
std::nullopt);
}
TEST_P(FeatureBindingsTest, RemoteSetDenied) {
PendingRemote<mojom::DefaultDenied> pending_remote;
auto pending_receiver = pending_remote.InitWithNewPipeAndPassReceiver();
RemoteSet<mojom::DefaultDenied> remote_set;
// Do not test the non-pending case as disabled remote<> cannot be bound.
EXPECT_EQ(remote_set.Add(std::move(pending_remote)), std::nullopt);
}
#endif // !DCHECK_IS_ON()
////
// Death tests - these are flaky on Android.
////
#if defined(GTEST_HAS_DEATH_TEST) && !BUILDFLAG(IS_ANDROID)
using FeatureBindingsDeathTest = FeatureBindingsTest;
TEST_P(FeatureBindingsDeathTest, MethodsOnRemoteDenied) {
// Validate that disabled methods on `FeaturesOnMethods` cannot be called.
bool called = false;
Remote<mojom::FeaturesOnMethods> remote;
FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver());
EXPECT_DEATH(remote->DefaultDenied(
base::BindLambdaForTesting([&](int) { called = true; })),
"");
EXPECT_FALSE(called);
}
TEST_P(FeatureBindingsDeathTest, MethodsOnRemoteDeniedSync) {
// Validate that disabled sync methods on remotes cannot be called.
int result = 0;
Remote<mojom::FeaturesOnMethods> remote;
FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver());
EXPECT_DEATH(remote->DefaultDeniedSync(&result), "");
EXPECT_EQ(result, 0);
}
TEST_P(FeatureBindingsDeathTest, MethodsOnAssociatedRemoteDenied) {
bool called = false;
// Validate that disabled methods on associated remotes cannot be called.
AssociatedRemote<mojom::FeaturesOnMethods> remote;
FeaturesOnMethodsImpl impl(remote.BindNewEndpointAndPassDedicatedReceiver());
EXPECT_DEATH(remote->DefaultDenied(
base::BindLambdaForTesting([&](int) { called = true; })),
"");
EXPECT_FALSE(called);
}
INSTANTIATE_TEST_SUITE_P(
,
FeatureBindingsDeathTest,
testing::Values(mojo::BindingsTestSerializationMode::kNeverSerialize));
#endif // defined(GTEST_HAS_DEATH_TEST) && !BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(
,
FeatureBindingsTest,
testing::Values(
mojo::BindingsTestSerializationMode::kSerializeBeforeSend,
mojo::BindingsTestSerializationMode::kSerializeBeforeDispatch,
mojo::BindingsTestSerializationMode::kNeverSerialize));
} // namespace mojo::test::feature_unittest