blob: 313f5712d38b0be79766f6422d44b530ae6ad23e [file] [log] [blame] [edit]
/*
* Copyright (C) 2026 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "MessageLog.h"
#include "Test.h"
#include <thread>
#include <wtf/Vector.h>
namespace TestWebKitAPI {
TEST(MessageLogTests, InitialState)
{
IPC::MessageLog<8> buffer;
// Verify buffer is initialized with Invalid message names
EXPECT_EQ(buffer.indexForTesting(), 0u);
for (size_t i = 0; i < 8; ++i)
EXPECT_EQ(buffer.bufferForTesting()[i], IPC::MessageName::Invalid);
}
TEST(MessageLogTests, AddSingleMessage)
{
IPC::MessageLog<8> buffer;
buffer.add(IPC::MessageName::IPCTester_EmptyMessage);
EXPECT_EQ(buffer.indexForTesting(), 1u);
EXPECT_EQ(buffer.bufferForTesting()[0], IPC::MessageName::IPCTester_EmptyMessage);
// Rest should still be Invalid
for (size_t i = 1; i < 8; ++i)
EXPECT_EQ(buffer.bufferForTesting()[i], IPC::MessageName::Invalid);
}
TEST(MessageLogTests, AddMultipleMessages)
{
IPC::MessageLog<8> buffer;
buffer.add(IPC::MessageName::IPCTester_EmptyMessage);
buffer.add(IPC::MessageName::IPCStreamTester_AsyncPing);
buffer.add(IPC::MessageName::IPCTester_AsyncPing);
EXPECT_EQ(buffer.indexForTesting(), 3u);
EXPECT_EQ(buffer.bufferForTesting()[0], IPC::MessageName::IPCTester_EmptyMessage);
EXPECT_EQ(buffer.bufferForTesting()[1], IPC::MessageName::IPCStreamTester_AsyncPing);
EXPECT_EQ(buffer.bufferForTesting()[2], IPC::MessageName::IPCTester_AsyncPing);
}
TEST(MessageLogTests, WrapAroundAtCapacity)
{
IPC::MessageLog<4> buffer;
// Fill the buffer
buffer.add(IPC::MessageName::IPCTester_EmptyMessage);
buffer.add(IPC::MessageName::IPCStreamTester_AsyncPing);
buffer.add(IPC::MessageName::IPCTester_AsyncPing);
buffer.add(IPC::MessageName::IPCStreamTester_EmptyMessage);
EXPECT_EQ(buffer.indexForTesting(), 4u); // Free-running index
EXPECT_EQ(buffer.bufferForTesting()[0], IPC::MessageName::IPCTester_EmptyMessage);
EXPECT_EQ(buffer.bufferForTesting()[1], IPC::MessageName::IPCStreamTester_AsyncPing);
EXPECT_EQ(buffer.bufferForTesting()[2], IPC::MessageName::IPCTester_AsyncPing);
EXPECT_EQ(buffer.bufferForTesting()[3], IPC::MessageName::IPCStreamTester_EmptyMessage);
// Add one more, should overwrite index 0
buffer.add(IPC::MessageName::IPCTester_CreateStreamTester);
EXPECT_EQ(buffer.indexForTesting(), 5u); // Free-running index
EXPECT_EQ(buffer.bufferForTesting()[0], IPC::MessageName::IPCTester_CreateStreamTester);
EXPECT_EQ(buffer.bufferForTesting()[1], IPC::MessageName::IPCStreamTester_AsyncPing);
}
TEST(MessageLogTests, MultipleWraps)
{
IPC::MessageLog<4> buffer;
// Add 10 messages to a buffer of size 4
for (size_t i = 0; i < 10; ++i)
buffer.add(IPC::MessageName::IPCTester_EmptyMessage);
// Free-running index should be 10
EXPECT_EQ(buffer.indexForTesting(), 10u);
// All entries should have the test message
for (size_t i = 0; i < 4; ++i)
EXPECT_EQ(buffer.bufferForTesting()[i], IPC::MessageName::IPCTester_EmptyMessage);
}
TEST(MessageLogTests, ConcurrentAddFromTwoThreads)
{
constexpr size_t bufferSize = 256; // Power of two
constexpr size_t messagesPerThread = bufferSize / 2;
IPC::MessageLog<bufferSize> buffer;
auto thread1Message = IPC::MessageName::IPCTester_EmptyMessage;
auto thread2Message = IPC::MessageName::IPCStreamTester_AsyncPing;
std::thread thread1([&buffer, thread1Message]() {
for (size_t i = 0; i < messagesPerThread; ++i)
buffer.add(thread1Message);
});
std::thread thread2([&buffer, thread2Message]() {
for (size_t i = 0; i < messagesPerThread; ++i)
buffer.add(thread2Message);
});
thread1.join();
thread2.join();
// Total messages added: 256
// Buffer size: 256, so no wrapping
// Expected final index: 256 (free-running)
EXPECT_EQ(buffer.indexForTesting(), bufferSize);
// Count occurrences of each message type in the buffer
size_t thread1Count = 0;
size_t thread2Count = 0;
for (size_t i = 0; i < bufferSize; ++i) {
if (buffer.bufferForTesting()[i] == thread1Message)
thread1Count++;
else if (buffer.bufferForTesting()[i] == thread2Message)
thread2Count++;
else
FAIL() << "Unexpected message name at index " << i << ": expected either thread1 or thread2 message";
}
// Each thread should have written exactly messagesPerThread entries
EXPECT_EQ(thread1Count, messagesPerThread);
EXPECT_EQ(thread2Count, messagesPerThread);
// Total should equal buffer size
EXPECT_EQ(thread1Count + thread2Count, bufferSize);
}
TEST(MessageLogTests, ConcurrentAddFromMultipleThreads)
{
constexpr size_t bufferSize = 512; // Power of two
constexpr size_t numThreads = 8;
constexpr size_t messagesPerThread = bufferSize / numThreads;
IPC::MessageLog<bufferSize> buffer;
Vector<std::thread> threads;
Vector<IPC::MessageName> messageNames = {
IPC::MessageName::IPCTester_EmptyMessage,
IPC::MessageName::IPCStreamTester_AsyncPing,
IPC::MessageName::IPCTester_AsyncPing,
IPC::MessageName::IPCStreamTester_EmptyMessage,
IPC::MessageName::IPCTester_CreateStreamTester,
IPC::MessageName::IPCTester_CreateConnectionTester,
IPC::MessageName::IPCTester_StartMessageTesting,
IPC::MessageName::IPCTester_CheckTestParameter
};
for (size_t t = 0; t < numThreads; ++t) {
threads.append(std::thread([&buffer, messageName = messageNames[t]]() {
for (size_t i = 0; i < messagesPerThread; ++i)
buffer.add(messageName);
}));
}
for (auto& thread : threads)
thread.join();
// Total messages: 8 * 64 = 512
// Buffer size: 512, no wrapping
// Expected index: 512 (free-running)
EXPECT_EQ(buffer.indexForTesting(), bufferSize);
// Count messages from each thread
Vector<size_t> messageCounts(numThreads, 0);
for (size_t i = 0; i < bufferSize; ++i) {
bool found = false;
for (size_t t = 0; t < numThreads; ++t) {
if (buffer.bufferForTesting()[i] == messageNames[t]) {
messageCounts[t]++;
found = true;
break;
}
}
EXPECT_TRUE(found) << "Unexpected message at index " << i;
}
// Each thread should have written exactly messagesPerThread entries
for (size_t t = 0; t < numThreads; ++t) {
EXPECT_EQ(messageCounts[t], messagesPerThread)
<< "Thread " << t << " wrote " << messageCounts[t]
<< " messages, expected " << messagesPerThread;
}
}
TEST(MessageLogTests, ConcurrentAddWithWrapping)
{
constexpr size_t bufferSize = 64;
constexpr size_t numThreads = 4;
constexpr size_t messagesPerThread = 100; // Total = 400, wraps multiple times
IPC::MessageLog<bufferSize> buffer;
Vector<std::thread> threads;
Vector<IPC::MessageName> messageNames;
// Each thread uses a unique message name
for (size_t t = 0; t < numThreads; ++t)
messageNames.append(static_cast<IPC::MessageName>(static_cast<uint16_t>(IPC::MessageName::IPCTester_EmptyMessage) + t));
for (size_t t = 0; t < numThreads; ++t) {
threads.append(std::thread([&buffer, messageName = messageNames[t]]() {
for (size_t i = 0; i < messagesPerThread; ++i)
buffer.add(messageName);
}));
}
for (auto& thread : threads)
thread.join();
// Free-running index should reflect all messages
EXPECT_EQ(buffer.indexForTesting(), numThreads * messagesPerThread);
// Every slot should contain one of the valid message names (no corruption from concurrent wrapping)
for (size_t i = 0; i < bufferSize; ++i) {
bool found = false;
for (size_t t = 0; t < numThreads; ++t) {
if (buffer.bufferForTesting()[i] == messageNames[t]) {
found = true;
break;
}
}
EXPECT_TRUE(found) << "Unexpected message at index " << i;
}
}
} // namespace TestWebKitAPI