blob: dd30eadb977c749cfe77bc176fa483fd9060ee68 [file] [log] [blame]
// Copyright 2013 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.
// This file tests the C API.
#include "mojo/public/c/system/core.h"
#include <stdint.h>
#include <string.h>
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/cpp/system/wait.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace {
const MojoHandleSignals kSignalReadadableWritable =
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
const MojoHandleSignals kSignalAll =
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE |
MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE |
MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED;
TEST(CoreAPITest, GetTimeTicksNow) {
const MojoTimeTicks start = MojoGetTimeTicksNow();
EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
<< "MojoGetTimeTicksNow should return nonzero value";
}
// The only handle that's guaranteed to be invalid is |MOJO_HANDLE_INVALID|.
// Tests that everything that takes a handle properly recognizes it.
TEST(CoreAPITest, InvalidHandle) {
MojoHandle h0, h1;
char buffer[10] = {0};
uint32_t buffer_size;
void* write_pointer;
const void* read_pointer;
// Close:
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID));
// Message pipe:
h0 = MOJO_HANDLE_INVALID;
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoWriteMessage(h0, MOJO_MESSAGE_HANDLE_INVALID, nullptr));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoReadMessage(h0, nullptr, nullptr));
// Data pipe:
buffer_size = static_cast<uint32_t>(sizeof(buffer));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoWriteData(h0, buffer, &buffer_size, nullptr));
write_pointer = nullptr;
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoBeginWriteData(h0, nullptr, &write_pointer, &buffer_size));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndWriteData(h0, 1, nullptr));
buffer_size = static_cast<uint32_t>(sizeof(buffer));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoReadData(h0, nullptr, buffer, &buffer_size));
read_pointer = nullptr;
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoBeginReadData(h0, nullptr, &read_pointer, &buffer_size));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndReadData(h0, 1, nullptr));
// Shared buffer:
h1 = MOJO_HANDLE_INVALID;
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoDuplicateBufferHandle(h0, nullptr, &h1));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoMapBuffer(h0, 0, 1, nullptr, &write_pointer));
}
TEST(CoreAPITest, BasicMessagePipe) {
MojoHandle h0, h1;
MojoHandleSignals sig;
h0 = MOJO_HANDLE_INVALID;
h1 = MOJO_HANDLE_INVALID;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &h0, &h1));
EXPECT_NE(h0, MOJO_HANDLE_INVALID);
EXPECT_NE(h1, MOJO_HANDLE_INVALID);
// Shouldn't be readable, we haven't written anything. Should be writable.
MojoHandleSignalsState state;
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
EXPECT_EQ(kSignalAll, state.satisfiable_signals);
// Try to read.
MojoMessageHandle message;
EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, MojoReadMessage(h0, nullptr, &message));
// Write to |h1|.
const uintptr_t kTestMessageContext = 1234;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message));
EXPECT_EQ(MOJO_RESULT_OK, MojoSetMessageContext(message, kTestMessageContext,
nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h1, message, nullptr));
// |h0| should be readable.
size_t result_index = 1;
MojoHandleSignalsState states[1];
sig = MOJO_HANDLE_SIGNAL_READABLE;
Handle handle0(h0);
EXPECT_EQ(MOJO_RESULT_OK,
mojo::WaitMany(&handle0, &sig, 1, &result_index, states));
EXPECT_EQ(0u, result_index);
EXPECT_EQ(kSignalReadadableWritable, states[0].satisfied_signals);
EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
// Read from |h0|.
EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(h0, nullptr, &message));
uintptr_t context;
EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageContext(message, nullptr, &context));
EXPECT_EQ(MOJO_RESULT_OK,
MojoSetMessageContext(message, 0, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message));
EXPECT_EQ(kTestMessageContext, context);
// |h0| should no longer be readable.
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
EXPECT_EQ(kSignalAll, state.satisfiable_signals);
// Close |h0|.
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(h1),
MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state));
// |h1| should no longer be readable or writable.
EXPECT_EQ(
MOJO_RESULT_FAILED_PRECONDITION,
mojo::Wait(mojo::Handle(h1),
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
&state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
EXPECT_FALSE(state.satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
EXPECT_FALSE(state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
}
TEST(CoreAPITest, BasicDataPipe) {
MojoHandle hp, hc;
MojoHandleSignals sig;
char buffer[20] = {0};
uint32_t buffer_size;
void* write_pointer;
const void* read_pointer;
hp = MOJO_HANDLE_INVALID;
hc = MOJO_HANDLE_INVALID;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &hp, &hc));
EXPECT_NE(hp, MOJO_HANDLE_INVALID);
EXPECT_NE(hc, MOJO_HANDLE_INVALID);
// The consumer |hc| shouldn't be readable.
MojoHandleSignalsState state;
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(hc, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
MOJO_HANDLE_SIGNAL_PEER_REMOTE,
state.satisfiable_signals);
// The producer |hp| should be writable.
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(hp, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
MOJO_HANDLE_SIGNAL_PEER_REMOTE,
state.satisfiable_signals);
// Try to read from |hc|.
buffer_size = static_cast<uint32_t>(sizeof(buffer));
EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
MojoReadData(hc, nullptr, buffer, &buffer_size));
// Try to begin a two-phase read from |hc|.
read_pointer = nullptr;
EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
MojoBeginReadData(hc, nullptr, &read_pointer, &buffer_size));
// Write to |hp|.
static const char kHello[] = "hello ";
// Don't include terminating null.
buffer_size = static_cast<uint32_t>(strlen(kHello));
EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(hp, kHello, &buffer_size, nullptr));
// |hc| should be(come) readable.
size_t result_index = 1;
MojoHandleSignalsState states[1];
sig = MOJO_HANDLE_SIGNAL_READABLE;
Handle consumer_handle(hc);
EXPECT_EQ(MOJO_RESULT_OK,
mojo::WaitMany(&consumer_handle, &sig, 1, &result_index, states));
EXPECT_EQ(0u, result_index);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
states[0].satisfied_signals);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE,
states[0].satisfiable_signals);
// Do a two-phase write to |hp|.
EXPECT_EQ(MOJO_RESULT_OK,
MojoBeginWriteData(hp, nullptr, &write_pointer, &buffer_size));
static const char kWorld[] = "world";
ASSERT_GE(buffer_size, sizeof(kWorld));
// Include the terminating null.
memcpy(write_pointer, kWorld, sizeof(kWorld));
EXPECT_EQ(
MOJO_RESULT_OK,
MojoEndWriteData(hp, static_cast<uint32_t>(sizeof(kWorld)), nullptr));
// Read one character from |hc|.
memset(buffer, 0, sizeof(buffer));
buffer_size = 1;
EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(hc, nullptr, buffer, &buffer_size));
// Close |hp|.
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hp));
// |hc| should still be readable.
EXPECT_EQ(MOJO_RESULT_OK, mojo::Wait(mojo::Handle(hc),
MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfied_signals);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfiable_signals);
// Do a two-phase read from |hc|.
read_pointer = nullptr;
EXPECT_EQ(MOJO_RESULT_OK,
MojoBeginReadData(hc, nullptr, &read_pointer, &buffer_size));
ASSERT_LE(buffer_size, sizeof(buffer) - 1);
memcpy(&buffer[1], read_pointer, buffer_size);
EXPECT_EQ(MOJO_RESULT_OK, MojoEndReadData(hc, buffer_size, nullptr));
EXPECT_STREQ("hello world", buffer);
// |hc| should no longer be readable.
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
mojo::Wait(mojo::Handle(hc), MOJO_HANDLE_SIGNAL_READABLE, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hc));
// TODO(vtl): Test the other way around -- closing the consumer should make
// the producer never-writable?
}
TEST(CoreAPITest, BasicSharedBuffer) {
MojoSharedBufferInfo buffer_info;
buffer_info.struct_size = sizeof(buffer_info);
MojoHandle h0 = MOJO_HANDLE_INVALID;
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoGetBufferInfo(h0, nullptr, &buffer_info));
// Create a shared buffer (|h0|).
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(100, nullptr, &h0));
EXPECT_NE(h0, MOJO_HANDLE_INVALID);
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoGetBufferInfo(h0, nullptr, nullptr));
ASSERT_EQ(MOJO_RESULT_OK, MojoGetBufferInfo(h0, nullptr, &buffer_info));
EXPECT_GE(buffer_info.size, 100U);
// Map everything.
void* pointer = nullptr;
EXPECT_EQ(MOJO_RESULT_OK, MojoMapBuffer(h0, 0, 100, nullptr, &pointer));
ASSERT_TRUE(pointer);
static_cast<char*>(pointer)[50] = 'x';
// Duplicate |h0| to |h1|.
MojoHandle h1 = MOJO_HANDLE_INVALID;
EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, nullptr, &h1));
EXPECT_NE(h1, MOJO_HANDLE_INVALID);
// Close |h0|.
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
// The mapping should still be good.
static_cast<char*>(pointer)[51] = 'y';
// Unmap it.
EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
// Map half of |h1|.
pointer = nullptr;
EXPECT_EQ(MOJO_RESULT_OK, MojoMapBuffer(h1, 50, 50, nullptr, &pointer));
ASSERT_TRUE(pointer);
// It should have what we wrote.
EXPECT_EQ('x', static_cast<char*>(pointer)[0]);
EXPECT_EQ('y', static_cast<char*>(pointer)[1]);
// Unmap it.
EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
buffer_info.size = 0;
ASSERT_EQ(MOJO_RESULT_OK, MojoGetBufferInfo(h1, nullptr, &buffer_info));
EXPECT_GE(buffer_info.size, 100U);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
}
// Defined in core_unittest_pure_c.c.
extern "C" const char* MinimalCTest(void);
// This checks that things actually work in C (not C++).
TEST(CoreAPITest, MinimalCTest) {
const char* failure = MinimalCTest();
EXPECT_FALSE(failure) << failure;
}
// TODO(vtl): Add multi-threaded tests.
} // namespace
} // namespace mojo