EDK: Add Dispatcher::SupportsEntrypointClass().
But don't actually use it yet.
The problem we need to solve is: With rights, we'd like to do the rights
check in a common place (in Core) for each entrypoint. However, in the
case that the rights check fails (i.e., the handle doesn't have the
required right(s)), we prefer to say "invalid argument" -- rather than
"permission denied" -- if the dispatcher does not support that
entrypoint.
So what we do is the following, if an entrypoint |Foo()| is called:
* Get the dispatcher |d|. (If this fails, then "invalid argument".)
* Do the rights check.
* If the rights check passes:
* Call |d->Foo()|.
* If |d| supports |Foo()|, it does its thing (and yields whatever
result).
* If not, then |d->Foo()| must return "invalid argument".
* If the rights check fails, then:
* Check |d->SupportsEntrypointClass(<class of Foo()>)|.
* If |Foo()|'s class is supported, then "permission denied".
* Else "invalid argument".
Note that we could of course call |d->SupportsEntrypointClass()| even
when the rights check passes. However, this is an additional call, and
penalizes the common/important (non-error) case, so we prefer to just
call |d->Foo()|.
R=azani@chromium.org
Review URL: https://codereview.chromium.org/1915153002 .
diff --git a/mojo/edk/system/core_test_base.cc b/mojo/edk/system/core_test_base.cc
index 542eadb..d73706d 100644
--- a/mojo/edk/system/core_test_base.cc
+++ b/mojo/edk/system/core_test_base.cc
@@ -32,9 +32,14 @@
return AdoptRef(new MockDispatcher(info));
}
- // |Dispatcher| private methods:
+ // |Dispatcher| public methods:
Type GetType() const override { return Type::UNKNOWN; }
+ bool SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const override {
+ return true;
+ }
+
private:
explicit MockDispatcher(CoreTestBase::MockHandleInfo* info) : info_(info) {
CHECK(info_);
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index 2b3aed0..521456e 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -27,6 +27,11 @@
return Type::DATA_PIPE_CONSUMER;
}
+bool DataPipeConsumerDispatcher::SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const {
+ return (entrypoint_class == EntrypointClass::DATA_PIPE_CONSUMER);
+}
+
// static
RefPtr<DataPipeConsumerDispatcher> DataPipeConsumerDispatcher::Deserialize(
Channel* channel,
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index 7b69568..ba69a35 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -29,6 +29,7 @@
// |Dispatcher| public methods:
Type GetType() const override;
+ bool SupportsEntrypointClass(EntrypointClass entrypoint_class) const override;
// The "opposite" of |SerializeAndClose()|. (Typically this is called by
// |Dispatcher::Deserialize()|.)
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index febe217..bfa7646 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -27,6 +27,11 @@
return Type::DATA_PIPE_PRODUCER;
}
+bool DataPipeProducerDispatcher::SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const {
+ return (entrypoint_class == EntrypointClass::DATA_PIPE_PRODUCER);
+}
+
// static
RefPtr<DataPipeProducerDispatcher> DataPipeProducerDispatcher::Deserialize(
Channel* channel,
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.h b/mojo/edk/system/data_pipe_producer_dispatcher.h
index 5f3e197..51675e7 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.h
@@ -29,6 +29,7 @@
// |Dispatcher| public methods:
Type GetType() const override;
+ bool SupportsEntrypointClass(EntrypointClass entrypoint_class) const override;
// The "opposite" of |SerializeAndClose()|. (Typically this is called by
// |Dispatcher::Deserialize()|.)
diff --git a/mojo/edk/system/dispatcher.h b/mojo/edk/system/dispatcher.h
index 2206848..e21a650 100644
--- a/mojo/edk/system/dispatcher.h
+++ b/mojo/edk/system/dispatcher.h
@@ -60,6 +60,9 @@
// |mutex()| method).
class Dispatcher : public util::RefCountedThreadSafe<Dispatcher> {
public:
+ // Types of dispatchers. Note that these are not necessarily a one-to-one with
+ // implementations of |Dispatcher|: multiple implementations may share the
+ // same type.
enum class Type {
UNKNOWN = 0,
MESSAGE_PIPE,
@@ -70,8 +73,41 @@
// "Private" types (not exposed via the public interface):
PLATFORM_HANDLE = -1
};
+
+ // Classes of "entrypoints"/"syscalls": Each dispatcher should support entire
+ // classes of methods (and if they don't support a given class, they should
+ // return |MOJO_RESULT_INVALID_ARGUMENT| for all the methods in that class).
+ // Warning: A method may be called even if |SupportsEntrypointClass()|
+ // indicates that the method's class is not supported (see below).
+ enum class EntrypointClass {
+ // |ReadMessage()|, |WriteMessage()|:
+ MESSAGE_PIPE,
+
+ // |SetDataPipeProducerOptions()|, |GetDataPipeProducerOptions()|,
+ // |WriteData()|, |BeginWriteData()|, |EndWriteData()|:
+ DATA_PIPE_PRODUCER,
+
+ // |SetDataPipeConsumerOptions()|, |GetDataPipeConsumerOptions()|,
+ // |ReadData()|, |BeginReadData()|, |EndReadData()|:
+ DATA_PIPE_CONSUMER,
+
+ // |DuplicateBufferHandle()|, |GetBufferInformation()|, |MapBuffer()|:
+ BUFFER,
+ };
+
+ // Gets the type of the dispatcher; see |Type| above.
virtual Type GetType() const = 0;
+ // Gets whether the given entrypoint class is supported; see |EntrypointClass|
+ // above. This is ONLY called when a rights check has failed, to determine
+ // whether |MOJO_RESULT_PERMISSION_DENIED| (if the entrypoint class is
+ // supported) or |MOJO_RESULT_INVALID_ARGUMENT| (if not) should be returned.
+ // In the case that the rights check passes, |Core| will proceed immediately
+ // to call the method (so if the method is not supported, it must still return
+ // |MOJO_RESULT_INVALID_ARGUMENT|).
+ virtual bool SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const = 0;
+
// These methods implement the various primitives named |Mojo...()|. These
// take |mutex_| and handle races with |Close()|. Then they call out to
// subclasses' |...ImplNoLock()| methods (still under |mutex_|), which
@@ -81,6 +117,7 @@
// possible. If this becomes an issue, we can rethink this.
MojoResult Close();
+ // |EntrypointClass::MESSAGE_PIPE|:
// |transports| may be non-null if and only if there are handles to be
// written; not that |this| must not be in |transports|. On success, all the
// dispatchers in |transports| must have been moved to a closed state; on
@@ -98,6 +135,7 @@
uint32_t* num_dispatchers,
MojoReadMessageFlags flags);
+ // |EntrypointClass::DATA_PIPE_PRODUCER|:
MojoResult SetDataPipeProducerOptions(
UserPointer<const MojoDataPipeProducerOptions> options);
MojoResult GetDataPipeProducerOptions(
@@ -110,6 +148,8 @@
UserPointer<uint32_t> buffer_num_bytes,
MojoWriteDataFlags flags);
MojoResult EndWriteData(uint32_t num_bytes_written);
+
+ // |EntrypointClass::DATA_PIPE_CONSUMER|:
MojoResult SetDataPipeConsumerOptions(
UserPointer<const MojoDataPipeConsumerOptions> options);
MojoResult GetDataPipeConsumerOptions(
@@ -123,6 +163,7 @@
MojoReadDataFlags flags);
MojoResult EndReadData(uint32_t num_bytes_read);
+ // |EntrypointClass::BUFFER|:
// |options| may be null. |new_dispatcher| must not be null, but
// |*new_dispatcher| should be null (and will contain the dispatcher for the
// new handle on success).
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index 7028cbe..005b094 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -71,6 +71,11 @@
return Type::MESSAGE_PIPE;
}
+bool MessagePipeDispatcher::SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const {
+ return (entrypoint_class == EntrypointClass::MESSAGE_PIPE);
+}
+
// static
RefPtr<MessagePipeDispatcher> MessagePipeDispatcher::CreateRemoteMessagePipe(
RefPtr<ChannelEndpoint>* channel_endpoint) {
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 054fe0f..be86377 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -47,6 +47,7 @@
// |Dispatcher| public methods:
Type GetType() const override;
+ bool SupportsEntrypointClass(EntrypointClass entrypoint_class) const override;
// Creates a |MessagePipe| with a local endpoint (at port 0) and a proxy
// endpoint, and creates/initializes a |MessagePipeDispatcher| (attached to
diff --git a/mojo/edk/system/message_pipe_dispatcher_unittest.cc b/mojo/edk/system/message_pipe_dispatcher_unittest.cc
index 171c84c..4bae645 100644
--- a/mojo/edk/system/message_pipe_dispatcher_unittest.cc
+++ b/mojo/edk/system/message_pipe_dispatcher_unittest.cc
@@ -38,10 +38,6 @@
namespace system {
namespace {
-const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE |
- MOJO_HANDLE_SIGNAL_WRITABLE |
- MOJO_HANDLE_SIGNAL_PEER_CLOSED;
-
TEST(MessagePipeDispatcherTest, Basic) {
Stopwatch stopwatch;
int32_t buffer[1];
@@ -70,7 +66,9 @@
EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Shouldn't need to remove the waiter (it was not added).
// Add a readable waiter to |d0|, then make it readable (by writing to
@@ -90,7 +88,9 @@
d0->RemoveAwakable(&w, &hss);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Try adding a readable waiter when already readable (from above).
w.Init();
@@ -99,7 +99,9 @@
d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 2, &hss));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Shouldn't need to remove the waiter (it was not added).
// Make |d0| no longer readable (by reading from it).
@@ -122,7 +124,9 @@
hss = HandleSignalsState();
d0->RemoveAwakable(&w, &hss);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Wait for non-zero, finite time for readability on |d0| (will time out).
w.Init();
@@ -137,7 +141,9 @@
hss = HandleSignalsState();
d0->RemoveAwakable(&w, &hss);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Check the peer closed signal.
w.Init();
@@ -159,6 +165,35 @@
}
}
+TEST(MessagePipeDispatcherTest, SupportsEntrypointClass) {
+ auto d = MessagePipeDispatcher::Create(
+ MessagePipeDispatcher::kDefaultCreateOptions);
+ ASSERT_TRUE(d);
+
+ // We need to initialize |d|.
+ {
+ auto d_peer = MessagePipeDispatcher::Create(
+ MessagePipeDispatcher::kDefaultCreateOptions);
+ auto mp = MessagePipe::CreateLocalLocal();
+ d->Init(mp.Clone(), 0);
+ d_peer->Init(std::move(mp), 1);
+ EXPECT_EQ(MOJO_RESULT_OK, d_peer->Close());
+ }
+
+ EXPECT_TRUE(
+ d->SupportsEntrypointClass(Dispatcher::EntrypointClass::MESSAGE_PIPE));
+ EXPECT_FALSE(d->SupportsEntrypointClass(
+ Dispatcher::EntrypointClass::DATA_PIPE_PRODUCER));
+ EXPECT_FALSE(d->SupportsEntrypointClass(
+ Dispatcher::EntrypointClass::DATA_PIPE_CONSUMER));
+ EXPECT_FALSE(d->SupportsEntrypointClass(Dispatcher::EntrypointClass::BUFFER));
+
+ // TODO(vtl): Check that it actually returns |MOJO_RESULT_INVALID_ARGUMENT|
+ // for methods in unsupported entrypoint classes.
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
TEST(MessagePipeDispatcherTest, InvalidParams) {
char buffer[1];
@@ -261,7 +296,9 @@
d0->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Try reading from |d1|; should fail (nothing to read).
buffer[0] = 0;
@@ -394,7 +431,9 @@
EXPECT_EQ(1u, context);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Now |d1| is already readable. Try waiting for it again.
{
@@ -409,7 +448,9 @@
EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
hss.satisfied_signals);
- EXPECT_EQ(kAllSignals, hss.satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
// Consume what we wrote to |d0|.
buffer[0] = 0;
diff --git a/mojo/edk/system/mock_simple_dispatcher.cc b/mojo/edk/system/mock_simple_dispatcher.cc
index 03828f6..bd8b283 100644
--- a/mojo/edk/system/mock_simple_dispatcher.cc
+++ b/mojo/edk/system/mock_simple_dispatcher.cc
@@ -50,6 +50,11 @@
return Type::UNKNOWN;
}
+bool MockSimpleDispatcher::SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const {
+ return false;
+}
+
MockSimpleDispatcher::MockSimpleDispatcher(
MojoHandleSignals satisfied_signals,
MojoHandleSignals satisfiable_signals)
diff --git a/mojo/edk/system/mock_simple_dispatcher.h b/mojo/edk/system/mock_simple_dispatcher.h
index 4f616b0..9776bd6 100644
--- a/mojo/edk/system/mock_simple_dispatcher.h
+++ b/mojo/edk/system/mock_simple_dispatcher.h
@@ -22,6 +22,7 @@
// Note: Use |MakeRefCounted<MockSimpleDispatcher>()|.
Type GetType() const override;
+ bool SupportsEntrypointClass(EntrypointClass entrypoint_class) const override;
void SetSatisfiedSignals(MojoHandleSignals new_satisfied_signals);
void SetSatisfiableSignals(MojoHandleSignals new_satisfiable_signals);
diff --git a/mojo/edk/system/platform_handle_dispatcher.cc b/mojo/edk/system/platform_handle_dispatcher.cc
index aab08d7..f2c6e72 100644
--- a/mojo/edk/system/platform_handle_dispatcher.cc
+++ b/mojo/edk/system/platform_handle_dispatcher.cc
@@ -35,6 +35,11 @@
return Type::PLATFORM_HANDLE;
}
+bool PlatformHandleDispatcher::SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const {
+ return false;
+}
+
// static
RefPtr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize(
Channel* channel,
@@ -73,8 +78,7 @@
ScopedPlatformHandle platform_handle)
: platform_handle_(platform_handle.Pass()) {}
-PlatformHandleDispatcher::~PlatformHandleDispatcher() {
-}
+PlatformHandleDispatcher::~PlatformHandleDispatcher() {}
void PlatformHandleDispatcher::CloseImplNoLock() {
mutex().AssertHeld();
diff --git a/mojo/edk/system/platform_handle_dispatcher.h b/mojo/edk/system/platform_handle_dispatcher.h
index 426f0c3..5190927 100644
--- a/mojo/edk/system/platform_handle_dispatcher.h
+++ b/mojo/edk/system/platform_handle_dispatcher.h
@@ -29,6 +29,7 @@
// |Dispatcher| public methods:
Type GetType() const override;
+ bool SupportsEntrypointClass(EntrypointClass entrypoint_class) const override;
// The "opposite" of |SerializeAndClose()|. (Typically this is called by
// |Dispatcher::Deserialize()|.)
diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
index a930c47..e57c511 100644
--- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
@@ -60,6 +60,34 @@
EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
}
+TEST(PlatformHandleDispatcher, SupportsEntrypointClass) {
+ test::ScopedTestDir test_dir;
+
+ util::ScopedFILE fp(test_dir.CreateFile());
+ ASSERT_TRUE(fp);
+
+ ScopedPlatformHandle h(PlatformHandleFromFILE(std::move(fp)));
+ EXPECT_FALSE(fp);
+ ASSERT_TRUE(h.is_valid());
+
+ auto d = PlatformHandleDispatcher::Create(h.Pass());
+ ASSERT_TRUE(d);
+ EXPECT_FALSE(h.is_valid());
+
+ EXPECT_FALSE(
+ d->SupportsEntrypointClass(Dispatcher::EntrypointClass::MESSAGE_PIPE));
+ EXPECT_FALSE(d->SupportsEntrypointClass(
+ Dispatcher::EntrypointClass::DATA_PIPE_PRODUCER));
+ EXPECT_FALSE(d->SupportsEntrypointClass(
+ Dispatcher::EntrypointClass::DATA_PIPE_CONSUMER));
+ EXPECT_FALSE(d->SupportsEntrypointClass(Dispatcher::EntrypointClass::BUFFER));
+
+ // TODO(vtl): Check that it actually returns |MOJO_RESULT_INVALID_ARGUMENT|
+ // for methods in unsupported entrypoint classes.
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
TEST(PlatformHandleDispatcherTest, CreateEquivalentDispatcherAndClose) {
test::ScopedTestDir test_dir;
diff --git a/mojo/edk/system/shared_buffer_dispatcher.cc b/mojo/edk/system/shared_buffer_dispatcher.cc
index 4474c70..3ea46d80 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.cc
+++ b/mojo/edk/system/shared_buffer_dispatcher.cc
@@ -97,6 +97,11 @@
return Type::SHARED_BUFFER;
}
+bool SharedBufferDispatcher::SupportsEntrypointClass(
+ EntrypointClass entrypoint_class) const {
+ return (entrypoint_class == EntrypointClass::BUFFER);
+}
+
// static
RefPtr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize(
Channel* channel,
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index 3ba0fff..3f2f6e7 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -56,6 +56,7 @@
// |Dispatcher| public methods:
Type GetType() const override;
+ bool SupportsEntrypointClass(EntrypointClass entrypoint_class) const override;
// The "opposite" of |SerializeAndClose()|. (Typically this is called by
// |Dispatcher::Deserialize()|.)
diff --git a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
index b0cc9a4..276a0b4 100644
--- a/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
+++ b/mojo/edk/system/shared_buffer_dispatcher_unittest.cc
@@ -183,6 +183,28 @@
EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[kSize / 2 + 1]);
}
+TEST_F(SharedBufferDispatcherTest, SupportsEntrypointClass) {
+ MojoResult result = MOJO_RESULT_INTERNAL;
+ auto d = SharedBufferDispatcher::Create(
+ platform_support(), SharedBufferDispatcher::kDefaultCreateOptions, 100u,
+ &result);
+ ASSERT_TRUE(d);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+
+ EXPECT_FALSE(
+ d->SupportsEntrypointClass(Dispatcher::EntrypointClass::MESSAGE_PIPE));
+ EXPECT_FALSE(d->SupportsEntrypointClass(
+ Dispatcher::EntrypointClass::DATA_PIPE_PRODUCER));
+ EXPECT_FALSE(d->SupportsEntrypointClass(
+ Dispatcher::EntrypointClass::DATA_PIPE_CONSUMER));
+ EXPECT_TRUE(d->SupportsEntrypointClass(Dispatcher::EntrypointClass::BUFFER));
+
+ // TODO(vtl): Check that it actually returns |MOJO_RESULT_INVALID_ARGUMENT|
+ // for methods in unsupported entrypoint classes.
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandle) {
const uint64_t kSize = 100u;