diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-13 22:40:06 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-13 22:40:06 +0000 |
commit | d1e0c221a522f1e9a4691faf4bee7fb6a3de99de (patch) | |
tree | 97286786eb1927e7d87d3f40c1bcbd593939fa22 /mojo | |
parent | 2ef8ba538b567d2c4e175a5cf55eb63a34778a6b (diff) | |
download | chromium_src-d1e0c221a522f1e9a4691faf4bee7fb6a3de99de.zip chromium_src-d1e0c221a522f1e9a4691faf4bee7fb6a3de99de.tar.gz chromium_src-d1e0c221a522f1e9a4691faf4bee7fb6a3de99de.tar.bz2 |
Mojo: Add a dispatcher that wraps a PlatformHandle (for use by the embedder).
Still to do: more tests -- test the embedder API and see if they can be
sent over a message pipe across processes (on POSIX; it's theoretically
implemented for POSIX and not yet implemented on Windows).
R=darin@chromium.org
Review URL: https://codereview.chromium.org/287663002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270230 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo')
-rw-r--r-- | mojo/embedder/embedder.cc | 41 | ||||
-rw-r--r-- | mojo/embedder/embedder.h | 15 | ||||
-rw-r--r-- | mojo/mojo.gyp | 3 | ||||
-rw-r--r-- | mojo/system/core.cc | 27 | ||||
-rw-r--r-- | mojo/system/core.h | 32 | ||||
-rw-r--r-- | mojo/system/dispatcher.h | 5 | ||||
-rw-r--r-- | mojo/system/handle_table.cc | 1 | ||||
-rw-r--r-- | mojo/system/message_pipe_dispatcher.cc | 2 | ||||
-rw-r--r-- | mojo/system/message_pipe_dispatcher_unittest.cc | 1 | ||||
-rw-r--r-- | mojo/system/platform_handle_dispatcher.cc | 89 | ||||
-rw-r--r-- | mojo/system/platform_handle_dispatcher.h | 57 | ||||
-rw-r--r-- | mojo/system/platform_handle_dispatcher_unittest.cc | 102 |
12 files changed, 343 insertions, 32 deletions
diff --git a/mojo/embedder/embedder.cc b/mojo/embedder/embedder.cc index 16de202..61d7932 100644 --- a/mojo/embedder/embedder.cc +++ b/mojo/embedder/embedder.cc @@ -14,6 +14,7 @@ #include "mojo/system/message_in_transit.h" #include "mojo/system/message_pipe.h" #include "mojo/system/message_pipe_dispatcher.h" +#include "mojo/system/platform_handle_dispatcher.h" #include "mojo/system/raw_channel.h" namespace mojo { @@ -134,5 +135,45 @@ void DestroyChannelOnIOThread(ChannelInfo* channel_info) { delete channel_info; } +MojoResult CreatePlatformHandleWrapper( + ScopedPlatformHandle platform_handle, + MojoHandle* platform_handle_wrapper_handle) { + DCHECK(platform_handle_wrapper_handle); + + scoped_refptr<system::Dispatcher> dispatcher( + new system::PlatformHandleDispatcher(platform_handle.Pass())); + + system::Core* core = system::entrypoints::GetCore(); + DCHECK(core); + MojoHandle h = core->AddDispatcher(dispatcher); + if (h == MOJO_HANDLE_INVALID) { + LOG(ERROR) << "Handle table full"; + dispatcher->Close(); + return MOJO_RESULT_RESOURCE_EXHAUSTED; + } + + *platform_handle_wrapper_handle = h; + return MOJO_RESULT_OK; +} + +MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle, + ScopedPlatformHandle* platform_handle) { + DCHECK(platform_handle); + + system::Core* core = system::entrypoints::GetCore(); + DCHECK(core); + scoped_refptr<system::Dispatcher> dispatcher( + core->GetDispatcher(platform_handle_wrapper_handle)); + if (!dispatcher.get()) + return MOJO_RESULT_INVALID_ARGUMENT; + + if (dispatcher->GetType() != system::Dispatcher::kTypePlatformHandle) + return MOJO_RESULT_INVALID_ARGUMENT; + + *platform_handle = static_cast<system::PlatformHandleDispatcher*>( + dispatcher.get())->PassPlatformHandle().Pass(); + return MOJO_RESULT_OK; +} + } // namespace embedder } // namespace mojo diff --git a/mojo/embedder/embedder.h b/mojo/embedder/embedder.h index 9914104..148e5d6 100644 --- a/mojo/embedder/embedder.h +++ b/mojo/embedder/embedder.h @@ -56,6 +56,21 @@ MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle CreateChannel( MOJO_SYSTEM_IMPL_EXPORT void DestroyChannelOnIOThread( ChannelInfo* channel_info); +// Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking +// ownership of it). This |MojoHandle| can then, e.g., be passed through message +// pipes. Note: This takes ownership (and thus closes) |platform_handle| even on +// failure, which is different from what you'd expect from a Mojo API, but it +// makes for a more convenient embedder API. +MOJO_SYSTEM_IMPL_EXPORT MojoResult CreatePlatformHandleWrapper( + ScopedPlatformHandle platform_handle, + MojoHandle* platform_handle_wrapper_handle); +// Retrieves the |PlatformHandle| that was wrapped into a |MojoHandle| (using +// |CreatePlatformHandleWrapper()| above). Note that the |MojoHandle| must still +// be closed separately. +MOJO_SYSTEM_IMPL_EXPORT MojoResult PassWrappedPlatformHandle( + MojoHandle platform_handle_wrapper_handle, + ScopedPlatformHandle* platform_handle); + } // namespace embedder } // namespace mojo diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index 56c9997..dd167b8 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -188,6 +188,8 @@ 'system/message_pipe_dispatcher.h', 'system/message_pipe_endpoint.cc', 'system/message_pipe_endpoint.h', + 'system/platform_handle_dispatcher.cc', + 'system/platform_handle_dispatcher.h', 'system/proxy_message_pipe_endpoint.cc', 'system/proxy_message_pipe_endpoint.h', 'system/raw_channel.cc', @@ -243,6 +245,7 @@ 'system/message_pipe_dispatcher_unittest.cc', 'system/message_pipe_unittest.cc', 'system/multiprocess_message_pipe_unittest.cc', + 'system/platform_handle_dispatcher_unittest.cc', 'system/raw_channel_unittest.cc', 'system/raw_shared_buffer_unittest.cc', 'system/remote_message_pipe_unittest.cc', diff --git a/mojo/system/core.cc b/mojo/system/core.cc index 90e6d40..6a4dfdc 100644 --- a/mojo/system/core.cc +++ b/mojo/system/core.cc @@ -27,12 +27,11 @@ namespace system { // Implementation notes // -// Mojo primitives are implemented by the singleton |Core| object. Most -// calls are for a "primary" handle (the first argument). -// |Core::GetDispatcher()| is used to look up a |Dispatcher| object for a -// given handle. That object implements most primitives for that object. The -// wait primitives are not attached to objects and are implemented by |Core| -// itself. +// Mojo primitives are implemented by the singleton |Core| object. Most calls +// are for a "primary" handle (the first argument). |Core::GetDispatcher()| is +// used to look up a |Dispatcher| object for a given handle. That object +// implements most primitives for that object. The wait primitives are not +// attached to objects and are implemented by |Core| itself. // // Some objects have multiple handles associated to them, e.g., message pipes // (which have two). In such a case, there is still a |Dispatcher| (e.g., @@ -86,6 +85,14 @@ MojoHandle Core::AddDispatcher( return handle_table_.AddDispatcher(dispatcher); } +scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { + if (handle == MOJO_HANDLE_INVALID) + return NULL; + + base::AutoLock locker(handle_table_lock_); + return handle_table_.GetDispatcher(handle); +} + MojoTimeTicks Core::GetTimeTicksNow() { return base::TimeTicks::Now().ToInternalValue(); } @@ -505,14 +512,6 @@ MojoResult Core::UnmapBuffer(void* buffer) { return mapping_table_.RemoveMapping(buffer); } -scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { - if (handle == MOJO_HANDLE_INVALID) - return NULL; - - base::AutoLock locker(handle_table_lock_); - return handle_table_.GetDispatcher(handle); -} - // Note: We allow |handles| to repeat the same handle multiple times, since // different flags may be specified. // TODO(vtl): This incurs a performance cost in |RemoveWaiter()|. Analyze this diff --git a/mojo/system/core.h b/mojo/system/core.h index 7bce6e3..76828d6 100644 --- a/mojo/system/core.h +++ b/mojo/system/core.h @@ -22,11 +22,18 @@ class Dispatcher; // are thread-safe. class MOJO_SYSTEM_IMPL_EXPORT Core { public: - // These methods are only to be used by via the embedder API. + // These methods are only to be used by via the embedder API (and internally). Core(); virtual ~Core(); + + // Adds |dispatcher| to the handle table, returning the handle for it. Returns + // |MOJO_HANDLE_INVALID| on failure, namely if the handle table is full. MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher); + // Looks up the dispatcher for the given handle. Returns null if the handle is + // invalid. + scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle); + // System calls implementation. MojoTimeTicks GetTimeTicksNow(); MojoResult Close(MojoHandle handle); @@ -37,9 +44,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const MojoWaitFlags* flags, uint32_t num_handles, MojoDeadline deadline); - MojoResult CreateMessagePipe( - MojoHandle* message_pipe_handle0, - MojoHandle* message_pipe_handle1); + MojoResult CreateMessagePipe(MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1); MojoResult WriteMessage(MojoHandle message_pipe_handle, const void* bytes, uint32_t num_bytes, @@ -52,10 +58,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { MojoHandle* handles, uint32_t* num_handles, MojoReadMessageFlags flags); - MojoResult CreateDataPipe( - const MojoCreateDataPipeOptions* options, - MojoHandle* data_pipe_producer_handle, - MojoHandle* data_pipe_consumer_handle); + MojoResult CreateDataPipe(const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle); MojoResult WriteData(MojoHandle data_pipe_producer_handle, const void* elements, uint32_t* num_bytes, @@ -76,10 +81,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { MojoReadDataFlags flags); MojoResult EndReadData(MojoHandle data_pipe_consumer_handle, uint32_t num_bytes_read); - MojoResult CreateSharedBuffer( - const MojoCreateSharedBufferOptions* options, - uint64_t num_bytes, - MojoHandle* shared_buffer_handle); + MojoResult CreateSharedBuffer(const MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle); MojoResult DuplicateBufferHandle( MojoHandle buffer_handle, const MojoDuplicateBufferHandleOptions* options, @@ -94,10 +98,6 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { private: friend bool internal::ShutdownCheckNoLeaks(Core*); - // Looks up the dispatcher for the given handle. Returns null if the handle is - // invalid. - scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle); - // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic // validation of arguments. MojoResult WaitManyInternal(const MojoHandle* handles, diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h index 4e9e13f..3a28097 100644 --- a/mojo/system/dispatcher.h +++ b/mojo/system/dispatcher.h @@ -55,7 +55,10 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher : kTypeMessagePipe, kTypeDataPipeProducer, kTypeDataPipeConsumer, - kTypeSharedBuffer + kTypeSharedBuffer, + + // "Private" types (not exposed via the public interface): + kTypePlatformHandle = -1 }; virtual Type GetType() const = 0; diff --git a/mojo/system/handle_table.cc b/mojo/system/handle_table.cc index dd5a5f4..2e4d22a 100644 --- a/mojo/system/handle_table.cc +++ b/mojo/system/handle_table.cc @@ -111,6 +111,7 @@ MojoResult HandleTable::MarkBusyAndStartTransport( DCHECK(handles); DCHECK_LE(num_handles, kMaxMessageNumHandles); DCHECK(transports); + DCHECK_EQ(transports->size(), num_handles); std::vector<Entry*> entries(num_handles); diff --git a/mojo/system/message_pipe_dispatcher.cc b/mojo/system/message_pipe_dispatcher.cc index 6d82583..3cee844 100644 --- a/mojo/system/message_pipe_dispatcher.cc +++ b/mojo/system/message_pipe_dispatcher.cc @@ -203,7 +203,7 @@ bool MessagePipeDispatcher::EndSerializeAndCloseImplNoLock( Channel* channel, void* destination, size_t* actual_size, - std::vector<embedder::PlatformHandle>* platform_handles) { + std::vector<embedder::PlatformHandle>* /*platform_handles*/) { DCHECK(HasOneRef()); // Only one ref => no need to take the lock. // Convert the local endpoint to a proxy endpoint (moving the message queue). diff --git a/mojo/system/message_pipe_dispatcher_unittest.cc b/mojo/system/message_pipe_dispatcher_unittest.cc index 3ef26bf..bfd4ddb 100644 --- a/mojo/system/message_pipe_dispatcher_unittest.cc +++ b/mojo/system/message_pipe_dispatcher_unittest.cc @@ -41,6 +41,7 @@ TEST(MessagePipeDispatcherTest, Basic) { // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa. for (unsigned i = 0; i < 2; i++) { scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher()); + EXPECT_EQ(Dispatcher::kTypeMessagePipe, d0->GetType()); scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher()); { scoped_refptr<MessagePipe> mp(new MessagePipe()); diff --git a/mojo/system/platform_handle_dispatcher.cc b/mojo/system/platform_handle_dispatcher.cc new file mode 100644 index 0000000..1d7d9b1 --- /dev/null +++ b/mojo/system/platform_handle_dispatcher.cc @@ -0,0 +1,89 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/system/platform_handle_dispatcher.h" + +#include "base/logging.h" + +namespace mojo { +namespace system { + +namespace { + +const size_t kInvalidPlatformHandleIndex = static_cast<size_t>(-1); + +struct SerializedPlatformHandleDispatcher { + size_t platform_handle_index; // (Or |kInvalidPlatformHandleIndex|.) +}; + +} // namespace + +PlatformHandleDispatcher::PlatformHandleDispatcher( + embedder::ScopedPlatformHandle platform_handle) + : platform_handle_(platform_handle.Pass()) { +} + +embedder::ScopedPlatformHandle PlatformHandleDispatcher::PassPlatformHandle() { + base::AutoLock locker(lock()); + return platform_handle_.Pass(); +} + +Dispatcher::Type PlatformHandleDispatcher::GetType() const { + return kTypePlatformHandle; +} + +PlatformHandleDispatcher::~PlatformHandleDispatcher() { +} + +void PlatformHandleDispatcher::CloseImplNoLock() { + lock().AssertAcquired(); + platform_handle_.reset(); +} + +scoped_refptr<Dispatcher> + PlatformHandleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { + lock().AssertAcquired(); + return scoped_refptr<Dispatcher>( + new PlatformHandleDispatcher(platform_handle_.Pass())); +} + +void PlatformHandleDispatcher::StartSerializeImplNoLock( + Channel* /*channel*/, + size_t* max_size, + size_t* max_platform_handles) { + DCHECK(HasOneRef()); // Only one ref => no need to take the lock. + *max_size = sizeof(SerializedPlatformHandleDispatcher); + *max_platform_handles = 1; +} + +bool PlatformHandleDispatcher::EndSerializeAndCloseImplNoLock( + Channel* /*channel*/, + void* destination, + size_t* actual_size, + std::vector<embedder::PlatformHandle>* platform_handles) { + DCHECK(HasOneRef()); // Only one ref => no need to take the lock. + + SerializedPlatformHandleDispatcher* serialization = + static_cast<SerializedPlatformHandleDispatcher*>(destination); + if (platform_handle_.is_valid()) { + serialization->platform_handle_index = platform_handles->size(); + platform_handles->push_back(platform_handle_.release()); + } else { + serialization->platform_handle_index = kInvalidPlatformHandleIndex; + } + + *actual_size = sizeof(SerializedPlatformHandleDispatcher); + return true; +} + +MojoWaitFlags PlatformHandleDispatcher::SatisfiedFlagsNoLock() const { + return MOJO_WAIT_FLAG_NONE; +} + +MojoWaitFlags PlatformHandleDispatcher::SatisfiableFlagsNoLock() const { + return MOJO_WAIT_FLAG_NONE; +} + +} // namespace system +} // namespace mojo diff --git a/mojo/system/platform_handle_dispatcher.h b/mojo/system/platform_handle_dispatcher.h new file mode 100644 index 0000000..eef0649 --- /dev/null +++ b/mojo/system/platform_handle_dispatcher.h @@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ +#define MOJO_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ + +#include "base/macros.h" +#include "mojo/embedder/scoped_platform_handle.h" +#include "mojo/system/simple_dispatcher.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace system { + +// A dispatcher that simply wraps/transports a |PlatformHandle| (only for use by +// the embedder). +class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher + : public SimpleDispatcher { + public: + explicit PlatformHandleDispatcher( + embedder::ScopedPlatformHandle platform_handle); + + embedder::ScopedPlatformHandle PassPlatformHandle(); + + // |Dispatcher| public methods: + virtual Type GetType() const OVERRIDE; + + private: + virtual ~PlatformHandleDispatcher(); + + // |Dispatcher| protected methods: + virtual void CloseImplNoLock() OVERRIDE; + virtual scoped_refptr<Dispatcher> + CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE; + virtual void StartSerializeImplNoLock(Channel* channel, + size_t* max_size, + size_t* max_platform_handles) OVERRIDE; + virtual bool EndSerializeAndCloseImplNoLock( + Channel* channel, + void* destination, + size_t* actual_size, + std::vector<embedder::PlatformHandle>* platform_handles) OVERRIDE; + + // |SimpleDispatcher| methods: + virtual MojoWaitFlags SatisfiedFlagsNoLock() const OVERRIDE; + virtual MojoWaitFlags SatisfiableFlagsNoLock() const OVERRIDE; + + embedder::ScopedPlatformHandle platform_handle_; + + DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_ diff --git a/mojo/system/platform_handle_dispatcher_unittest.cc b/mojo/system/platform_handle_dispatcher_unittest.cc new file mode 100644 index 0000000..5c22f55 --- /dev/null +++ b/mojo/system/platform_handle_dispatcher_unittest.cc @@ -0,0 +1,102 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/system/platform_handle_dispatcher.h" + +#include <stdio.h> + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/memory/ref_counted.h" +#include "mojo/common/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace system { +namespace { + +TEST(PlatformHandleDispatcherTest, Basic) { + static const char kHelloWorld[] = "hello world"; + + base::FilePath unused; + base::ScopedFILE fp(CreateAndOpenTemporaryFile(&unused)); + ASSERT_TRUE(fp); + EXPECT_EQ(sizeof(kHelloWorld), + fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get())); + + embedder::ScopedPlatformHandle + h(mojo::test::PlatformHandleFromFILE(fp.Pass())); + EXPECT_FALSE(fp); + ASSERT_TRUE(h.is_valid()); + + scoped_refptr<PlatformHandleDispatcher> dispatcher( + new PlatformHandleDispatcher(h.Pass())); + EXPECT_FALSE(h.is_valid()); + EXPECT_EQ(Dispatcher::kTypePlatformHandle, dispatcher->GetType()); + + h = dispatcher->PassPlatformHandle().Pass(); + EXPECT_TRUE(h.is_valid()); + + fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass(); + EXPECT_FALSE(h.is_valid()); + EXPECT_TRUE(fp); + + rewind(fp.get()); + char read_buffer[1000] = {}; + EXPECT_EQ(sizeof(kHelloWorld), + fread(read_buffer, 1, sizeof(read_buffer), fp.get())); + EXPECT_STREQ(kHelloWorld, read_buffer); + + // Try getting the handle again. (It should fail cleanly.) + h = dispatcher->PassPlatformHandle().Pass(); + EXPECT_FALSE(h.is_valid()); + + EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); +} + +TEST(PlatformHandleDispatcherTest, CreateEquivalentDispatcherAndClose) { + static const char kFooBar[] = "foo bar"; + + base::FilePath unused; + base::ScopedFILE fp(CreateAndOpenTemporaryFile(&unused)); + EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get())); + + scoped_refptr<PlatformHandleDispatcher> dispatcher( + new PlatformHandleDispatcher( + mojo::test::PlatformHandleFromFILE(fp.Pass()))); + + DispatcherTransport transport( + test::DispatcherTryStartTransport(dispatcher.get())); + EXPECT_TRUE(transport.is_valid()); + EXPECT_EQ(Dispatcher::kTypePlatformHandle, transport.GetType()); + EXPECT_FALSE(transport.IsBusy()); + + scoped_refptr<Dispatcher> generic_dispatcher = + transport.CreateEquivalentDispatcherAndClose(); + ASSERT_TRUE(generic_dispatcher); + + transport.End(); + EXPECT_TRUE(dispatcher->HasOneRef()); + dispatcher = NULL; + + ASSERT_EQ(Dispatcher::kTypePlatformHandle, generic_dispatcher->GetType()); + dispatcher = static_cast<PlatformHandleDispatcher*>(generic_dispatcher.get()); + + fp = mojo::test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(), + "rb").Pass(); + EXPECT_TRUE(fp); + + rewind(fp.get()); + char read_buffer[1000] = {}; + EXPECT_EQ(sizeof(kFooBar), + fread(read_buffer, 1, sizeof(read_buffer), fp.get())); + EXPECT_STREQ(kFooBar, read_buffer); + + EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); +} + +} // namespace +} // namespace system +} // namespace mojo |