315 lines
11 KiB
C++
315 lines
11 KiB
C++
// Copyright 2018 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 <string>
|
|
|
|
#include "mojo/core/test/mojo_test_base.h"
|
|
#include "mojo/public/c/system/quota.h"
|
|
|
|
namespace mojo {
|
|
namespace core {
|
|
namespace {
|
|
|
|
using QuotaTest = test::MojoTestBase;
|
|
|
|
void QuotaExceededEventHandler(const MojoTrapEvent* event) {
|
|
// Always treat trigger context as the address of a bool to set to |true|.
|
|
if (event->result == MOJO_RESULT_OK)
|
|
*reinterpret_cast<bool*>(event->trigger_context) = true;
|
|
}
|
|
|
|
TEST_F(QuotaTest, InvalidArguments) {
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(MOJO_HANDLE_INVALID,
|
|
MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 2, nullptr));
|
|
|
|
const MojoQuotaType kInvalidQuotaType = 0xfffffffful;
|
|
MojoHandle message_pipe0, message_pipe1;
|
|
CreateMessagePipe(&message_pipe0, &message_pipe1);
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(message_pipe0, kInvalidQuotaType, 0, nullptr));
|
|
|
|
const MojoSetQuotaOptions kInvalidSetQuotaOptions = {0};
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(message_pipe0, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 0,
|
|
&kInvalidSetQuotaOptions));
|
|
|
|
uint64_t limit = 0;
|
|
uint64_t usage = 0;
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoQueryQuota(message_pipe0, kInvalidQuotaType, nullptr, &limit,
|
|
&usage));
|
|
|
|
const MojoQueryQuotaOptions kInvalidQueryQuotaOptions = {0};
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoQueryQuota(message_pipe0, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH,
|
|
&kInvalidQueryQuotaOptions, &limit, &usage));
|
|
|
|
MojoClose(message_pipe0);
|
|
MojoClose(message_pipe1);
|
|
|
|
MojoHandle producer, consumer;
|
|
CreateDataPipe(&producer, &consumer, 1);
|
|
EXPECT_EQ(
|
|
MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(producer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 0, nullptr));
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(producer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, 0,
|
|
nullptr));
|
|
EXPECT_EQ(
|
|
MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(consumer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, 0, nullptr));
|
|
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
|
MojoSetQuota(consumer, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE, 0,
|
|
nullptr));
|
|
MojoClose(producer);
|
|
MojoClose(consumer);
|
|
}
|
|
|
|
TEST_F(QuotaTest, BasicReceiveQueueLength) {
|
|
MojoHandle a, b;
|
|
CreateMessagePipe(&a, &b);
|
|
|
|
uint64_t limit = 0;
|
|
uint64_t usage = 0;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(MOJO_QUOTA_LIMIT_NONE, limit);
|
|
EXPECT_EQ(0u, usage);
|
|
|
|
const uint64_t kTestLimit = 42;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoSetQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, kTestLimit,
|
|
nullptr));
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(kTestLimit, limit);
|
|
EXPECT_EQ(0u, usage);
|
|
|
|
const std::string kTestMessage = "doot";
|
|
WriteMessage(b, kTestMessage);
|
|
WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(kTestLimit, limit);
|
|
EXPECT_EQ(1u, usage);
|
|
}
|
|
|
|
TEST_F(QuotaTest, BasicReceiveQueueMemorySize) {
|
|
MojoHandle a, b;
|
|
CreateMessagePipe(&a, &b);
|
|
|
|
uint64_t limit = 0;
|
|
uint64_t usage = 0;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
nullptr, &limit, &usage));
|
|
EXPECT_EQ(MOJO_QUOTA_LIMIT_NONE, limit);
|
|
EXPECT_EQ(0u, usage);
|
|
|
|
const uint64_t kTestLimit = 42;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoSetQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
kTestLimit, nullptr));
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
nullptr, &limit, &usage));
|
|
EXPECT_EQ(kTestLimit, limit);
|
|
EXPECT_EQ(0u, usage);
|
|
|
|
const std::string kTestMessage = "doot";
|
|
WriteMessage(b, kTestMessage);
|
|
WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(a, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
nullptr, &limit, &usage));
|
|
EXPECT_EQ(kTestLimit, limit);
|
|
EXPECT_EQ(usage, kTestMessage.size());
|
|
|
|
MojoClose(a);
|
|
MojoClose(b);
|
|
}
|
|
|
|
TEST_F(QuotaTest, ReceiveQueueLengthLimitExceeded) {
|
|
MojoHandle a, b;
|
|
CreateMessagePipe(&a, &b);
|
|
|
|
const uint64_t kMaxMessages = 1;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoSetQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, kMaxMessages,
|
|
nullptr));
|
|
|
|
MojoHandleSignalsState signals;
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
const std::string kTestMessage = "this message is lit, fam";
|
|
WriteMessage(a, kTestMessage);
|
|
WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE);
|
|
|
|
uint64_t limit = 0;
|
|
uint64_t usage = 0;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(kMaxMessages, limit);
|
|
EXPECT_EQ(1u, usage);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
// Push the endpoint over quota and ensure that it signals accordingly.
|
|
WriteMessage(a, kTestMessage);
|
|
WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(kMaxMessages, limit);
|
|
EXPECT_EQ(2u, usage);
|
|
|
|
// Read a message and wait for QUOTA_EXCEEDED to go back low.
|
|
EXPECT_EQ(kTestMessage, ReadMessage(b));
|
|
WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED,
|
|
MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(kMaxMessages, limit);
|
|
EXPECT_EQ(1u, usage);
|
|
|
|
MojoClose(a);
|
|
MojoClose(b);
|
|
}
|
|
|
|
TEST_F(QuotaTest, ReceiveQueueMemorySizeLimitExceeded) {
|
|
MojoHandle a, b;
|
|
CreateMessagePipe(&a, &b);
|
|
|
|
const uint64_t kMaxMessageBytes = 6;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoSetQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
kMaxMessageBytes, nullptr));
|
|
|
|
MojoHandleSignalsState signals;
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
const std::string kTestMessage = "four";
|
|
WriteMessage(a, kTestMessage);
|
|
WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE);
|
|
|
|
uint64_t limit = 0;
|
|
uint64_t usage = 0;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
nullptr, &limit, &usage));
|
|
EXPECT_EQ(kMaxMessageBytes, limit);
|
|
EXPECT_EQ(kTestMessage.size(), usage);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
// Push the endpoint over quota and ensure that it signals accordingly.
|
|
WriteMessage(a, kTestMessage);
|
|
WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
nullptr, &limit, &usage));
|
|
EXPECT_EQ(kMaxMessageBytes, limit);
|
|
EXPECT_EQ(kTestMessage.size() * 2, usage);
|
|
|
|
// Read a message and wait for QUOTA_EXCEEDED to go back low.
|
|
EXPECT_EQ(kTestMessage, ReadMessage(b));
|
|
WaitForSignals(b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED,
|
|
MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE,
|
|
nullptr, &limit, &usage));
|
|
EXPECT_EQ(kMaxMessageBytes, limit);
|
|
EXPECT_EQ(kTestMessage.size(), usage);
|
|
|
|
MojoClose(a);
|
|
MojoClose(b);
|
|
}
|
|
|
|
TEST_F(QuotaTest, TrapQuotaExceeded) {
|
|
// Simple sanity check to verify that QUOTA_EXCEEDED signals can be trapped
|
|
// like any other signals.
|
|
|
|
MojoHandle a, b;
|
|
CreateMessagePipe(&a, &b);
|
|
|
|
const uint64_t kMaxMessages = 42;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoSetQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, kMaxMessages,
|
|
nullptr));
|
|
|
|
bool signal_event_fired = false;
|
|
MojoHandle quota_trap;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoCreateTrap(&QuotaExceededEventHandler, nullptr, "a_trap));
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoAddTrigger(quota_trap, b, MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED,
|
|
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
|
|
reinterpret_cast<uintptr_t>(&signal_event_fired),
|
|
nullptr));
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(quota_trap, nullptr, nullptr, nullptr));
|
|
|
|
const std::string kTestMessage("sup");
|
|
for (uint64_t i = 0; i < kMaxMessages; ++i)
|
|
WriteMessage(a, kTestMessage);
|
|
|
|
// We're at quota but not yet over.
|
|
MojoHandleSignalsState signals;
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_FALSE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
EXPECT_FALSE(signal_event_fired);
|
|
|
|
// Push over quota. The event handler should be invoked before this returns.
|
|
WriteMessage(a, kTestMessage);
|
|
EXPECT_TRUE(signal_event_fired);
|
|
|
|
EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &signals));
|
|
EXPECT_TRUE(signals.satisfied_signals & MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED);
|
|
|
|
uint64_t limit = 0;
|
|
uint64_t usage = 0;
|
|
EXPECT_EQ(MOJO_RESULT_OK,
|
|
MojoQueryQuota(b, MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH, nullptr,
|
|
&limit, &usage));
|
|
EXPECT_EQ(kMaxMessages, limit);
|
|
EXPECT_EQ(kMaxMessages + 1, usage);
|
|
|
|
MojoClose(quota_trap);
|
|
MojoClose(a);
|
|
MojoClose(b);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace core
|
|
} // namespace mojo
|