chromium/mojo/public/cpp/bindings/tests/feature_unittest.cc

// 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 {};

class OtherDeniedImpl : public mojom::OtherDenied {};

class OtherAllowedImpl : public mojom::OtherAllowed {};

class DefaultDeniedSelfOwnedImpl : public mojom::DefaultDenied {};

class DefaultAllowedSelfOwnedImpl : public mojom::DefaultAllowed {};

class DefaultAllowedImpl : mojom::DefaultAllowed {};

class FeaturesOnMethodsImpl : public mojom::FeaturesOnMethods {};

class PassesInterfacesImpl : public mojom::PassesInterfaces {};

}  // namespace

//// Test runners ////

class FeatureBindingsTest : public BindingsTestBase {};

//// Tests /////

TEST() {
  EXPECT_TRUE();
  EXPECT_FALSE();
}

TEST() {
  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();
  EXPECT_TRUE();
}

//// Tests that enabled features allow normal behavior. ////

TEST_P() {
  // 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();

  base::RunLoop run_loop;
  remote->GetInt(base::BindLambdaForTesting([&](int) { run_loop.Quit(); }));
  run_loop.Run();
  EXPECT_FALSE();
}

TEST_P() {
  // 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();

  base::RunLoop run_loop;
  remote->GetInt(base::BindLambdaForTesting([&](int) { run_loop.Quit(); }));
  run_loop.Run();
  EXPECT_FALSE();
}

TEST_P() {
  // Validate that allowed interfaces on `FeaturesOnMethods` can be called.
  bool error = false;
  SetErrorHandler(base::BindLambdaForTesting([&] { error = true; }));

  Remote<mojom::FeaturesOnMethods> remote;
  EXPECT_FALSE();
  FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver());
  EXPECT_TRUE();

  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();
}

TEST_P() {
  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();
  EXPECT_TRUE();
}

TEST_P() {
  // 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();
  PassesInterfacesImpl impl(remote.BindNewPipeAndPassReceiver());
  EXPECT_TRUE();

  {
    // 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();
  }

  {
    // 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();
  }
}

TEST_P() {
  // 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() {
  // 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();
  EXPECT_FALSE();
}

////
// 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() {
  // Validate that disabled methods on `FeaturesOnMethods` cannot be called.
  bool called = false;
  Remote<mojom::FeaturesOnMethods> remote;
  FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver());
  EXPECT_DEATH();
  EXPECT_FALSE();
}

TEST_P() {
  // Validate that disabled sync methods on remotes cannot be called.
  int result = 0;
  Remote<mojom::FeaturesOnMethods> remote;
  FeaturesOnMethodsImpl impl(remote.BindNewPipeAndPassReceiver());
  EXPECT_DEATH();
  EXPECT_EQ();
}

TEST_P() {
  bool called = false;
  // Validate that disabled methods on associated remotes cannot be called.
  AssociatedRemote<mojom::FeaturesOnMethods> remote;
  FeaturesOnMethodsImpl impl(remote.BindNewEndpointAndPassDedicatedReceiver());
  EXPECT_DEATH();
  EXPECT_FALSE();
}

INSTANTIATE_TEST_SUITE_P();

#endif  // defined(GTEST_HAS_DEATH_TEST) && !BUILDFLAG(IS_ANDROID)

INSTANTIATE_TEST_SUITE_P();

}  // namespace mojo::test::feature_unittest