blob: 83b4b0ae2e41b25808214d3c50d85dbff5b24817 [file] [log] [blame]
// 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/edk/embedder/embedder.h"
#include "base/logging.h"
#include "mojo/edk/embedder/test_embedder.h"
#include "mojo/edk/system/test/test_io_thread.h"
#include "mojo/edk/system/test/timeouts.h"
#include "mojo/edk/util/mutex.h"
#include "mojo/edk/util/ref_ptr.h"
#include "mojo/edk/util/thread_annotations.h"
#include "mojo/edk/util/waitable_event.h"
#include "mojo/public/c/system/handle.h"
#include "mojo/public/c/system/result.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/macros.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "testing/gtest/include/gtest/gtest.h"
using mojo::system::test::TestIOThread;
using mojo::util::ManualResetWaitableEvent;
using mojo::util::Mutex;
using mojo::util::MutexLocker;
namespace mojo {
namespace embedder {
namespace {
class EmbedderTest : public testing::Test {
public:
EmbedderTest() : test_io_thread_(TestIOThread::StartMode::AUTO) {}
~EmbedderTest() override {}
protected:
TestIOThread& test_io_thread() { return test_io_thread_; }
private:
void SetUp() override { test::InitWithSimplePlatformSupport(); }
void TearDown() override { EXPECT_TRUE(test::Shutdown()); }
// TODO(vtl): We don't really need an I/O thread.
TestIOThread test_io_thread_;
MOJO_DISALLOW_COPY_AND_ASSIGN(EmbedderTest);
};
class TestAsyncWaiter {
public:
TestAsyncWaiter() : wait_result_(MOJO_RESULT_UNKNOWN) {}
void Awake(MojoResult result) {
MutexLocker l(&wait_result_mutex_);
wait_result_ = result;
event_.Signal();
}
bool TryWait() {
return !event_.WaitWithTimeout(mojo::system::test::ActionTimeout());
}
MojoResult wait_result() const {
MutexLocker l(&wait_result_mutex_);
return wait_result_;
}
private:
ManualResetWaitableEvent event_;
mutable Mutex wait_result_mutex_;
MojoResult wait_result_ MOJO_GUARDED_BY(wait_result_mutex_);
MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaiter);
};
TEST_F(EmbedderTest, AsyncWait) {
ScopedMessagePipeHandle client_mp;
ScopedMessagePipeHandle server_mp;
EXPECT_EQ(MOJO_RESULT_OK, CreateMessagePipe(nullptr, &client_mp, &server_mp));
TestAsyncWaiter waiter;
EXPECT_EQ(MOJO_RESULT_OK,
AsyncWait(client_mp.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
[&waiter](MojoResult result) { waiter.Awake(result); }));
// TODO(vtl): With C++14 lambda captures, we'll be able to avoid this
// nonsense.
{
auto server_mp_value = server_mp.get();
test_io_thread().PostTask([server_mp_value]() {
static const char kHello[] = "hello";
CHECK_EQ(MOJO_RESULT_OK,
WriteMessageRaw(server_mp_value, kHello,
static_cast<uint32_t>(sizeof(kHello)), nullptr,
0, MOJO_WRITE_MESSAGE_FLAG_NONE));
});
}
EXPECT_TRUE(waiter.TryWait());
EXPECT_EQ(MOJO_RESULT_OK, waiter.wait_result());
// If message is in the queue, it does't allow us to wait.
TestAsyncWaiter waiter_that_doesnt_wait;
EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
AsyncWait(client_mp.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
[&waiter_that_doesnt_wait](MojoResult result) {
waiter_that_doesnt_wait.Awake(result);
}));
char buffer[1000];
uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
CHECK_EQ(MOJO_RESULT_OK,
ReadMessageRaw(client_mp.get(), buffer, &num_bytes, nullptr, nullptr,
MOJO_READ_MESSAGE_FLAG_NONE));
TestAsyncWaiter unsatisfiable_waiter;
EXPECT_EQ(MOJO_RESULT_OK,
AsyncWait(client_mp.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
[&unsatisfiable_waiter](MojoResult result) {
unsatisfiable_waiter.Awake(result);
}));
// TODO(vtl): With C++14 lambda captures, we'll be able to avoid this
// nonsense (and use |Close()| rather than |CloseRaw()|).
{
auto server_mp_value = server_mp.release();
test_io_thread().PostTask(
[server_mp_value]() { CloseRaw(server_mp_value); });
}
EXPECT_TRUE(unsatisfiable_waiter.TryWait());
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
unsatisfiable_waiter.wait_result());
}
} // namespace
} // namespace embedder
} // namespace mojo