blob: 0a91091b230b6d70e5e4ae315888f266f01dbde4 [file]
/*
* Copyright (C) 2025 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 "Test.h"
#include <wtf/HexNumber.h>
#include <wtf/MemoryDump.h>
#include <wtf/StringPrintStream.h>
#include <wtf/text/StringBuilder.h>
namespace TestWebKitAPI {
TEST(WTF, MemoryDumpNullPointer)
{
// Test null data pointer case
MemoryDump nullDump(std::span(static_cast<std::byte*>(nullptr), 42));
EXPECT_EQ(nullDump.span().data(), nullptr);
EXPECT_EQ(nullDump.span().size(), 42u);
EXPECT_EQ(nullDump.sizeLimit(), MemoryDump::DefaultSizeLimit);
StringPrintStream stream;
stream.print(nullDump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
EXPECT_EQ(output, "\n00000000: (not dumping 42 bytes)"_s);
}
TEST(WTF, MemoryDumpEmptySize)
{
// Test zero size case
uint8_t data[] = { 0x41, 0x42, 0x43, 0x44 };
MemoryDump emptyDump(std::span<uint8_t>(data, 0));
EXPECT_EQ(emptyDump.span().data(), reinterpret_cast<const std::byte*>(data));
EXPECT_EQ(emptyDump.span().size(), 0u);
StringPrintStream stream;
stream.print(emptyDump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
EXPECT_TRUE(output.startsWith('\n'));
EXPECT_TRUE(output.contains(": (span is empty)"_s));
}
TEST(WTF, MemoryDumpSingleByte)
{
uint8_t data[] = { 0x42 }; // 'B'
MemoryDump dump { std::span<uint8_t>(data) };
StringPrintStream stream;
stream.print(dump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
EXPECT_TRUE(output.startsWith('\n'));
// Should contain the address, hex representation, and ASCII
EXPECT_TRUE(output.contains("42"_s));
EXPECT_TRUE(output.contains("B"_s));
}
TEST(WTF, MemoryDumpExactly16Bytes)
{
uint8_t data[16];
for (int i = 0; i < 16; ++i)
data[i] = static_cast<uint8_t>(0x41 + i); // A, B, C, ...
MemoryDump dump { std::span<uint8_t>(data) };
StringPrintStream stream;
stream.print(dump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
EXPECT_TRUE(output.startsWith('\n'));
// Verify hex formatting: should have space after 8th byte
Vector<String> parts = output.split(" "_s); // Split on double space before ASCII
EXPECT_EQ(parts.size(), 2u);
String hexPart = parts[0];
String asciiPart = parts[1];
// Verify ASCII part
EXPECT_EQ(asciiPart, "ABCDEFGHIJKLMNOP"_s);
// Verify hex part contains proper spacing
EXPECT_TRUE(hexPart.contains("41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50"_s));
}
TEST(WTF, MemoryDumpMultipleLines)
{
uint8_t data[33]; // More than 2 lines
for (int i = 0; i < 33; ++i)
data[i] = static_cast<uint8_t>(i);
MemoryDump dump { std::span<uint8_t>(data) };
StringPrintStream stream;
stream.print(dump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
auto lines = output.split('\n');
EXPECT_EQ(lines.size(), 3u);
// First line: 16 bytes (0x00-0x0f)
String line1 = lines[0];
EXPECT_TRUE(line1.contains("00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"_s));
// Second line: 16 bytes (0x10-0x1f)
String line2 = lines[1];
EXPECT_TRUE(line2.contains("10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f"_s));
// Third line: 1 byte (0x20) - should be padded
String line3 = lines[2];
EXPECT_TRUE(line3.contains("20"_s));
EXPECT_TRUE(line3.contains(" "_s)); // Should have padding spaces
}
TEST(WTF, MemoryDumpASCIIRepresentation)
{
// Test various ASCII ranges
uint8_t data[] = {
31, 32, 65, 90, 97, 122, 126, 127, // Control, space, A, Z, a, z, ~, DEL
0, 255, 128 // NUL, 0xFF, 0x80
};
MemoryDump dump { std::span<uint8_t>(data) };
StringPrintStream stream;
stream.print(dump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
Vector<String> parts = output.split(" "_s);
EXPECT_EQ(parts.size(), 2u);
String asciiPart = parts[1];
// Verify ASCII conversion:
// 31->'.', 32->' ', 65->'A', 90->'Z', 97->'a', 122->'z', 126->'~', 127->'.', 0->'.', 255->'.', 128->'.'
EXPECT_TRUE(asciiPart.contains(". AZaz~...."_s));
}
TEST(WTF, MemoryDumpSizeLimitTruncation)
{
constexpr size_t dataSize = 100;
constexpr size_t limitSize = 32; // Less than 2 full lines
uint8_t data[dataSize];
for (size_t i = 0; i < dataSize; ++i)
data[i] = static_cast<uint8_t>(i & 0xFF);
MemoryDump dump { std::span<uint8_t>(data), limitSize };
StringPrintStream stream;
stream.print(dump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
auto lines = output.split("\n"_s);
EXPECT_EQ(lines.size(), 3u); // two output lines and the truncation message
auto truncation = lines[2];
// Should contain truncation message
EXPECT_TRUE(truncation.contains("... (remaining 68 bytes not dumped)"_s));
}
TEST(WTF, MemoryDumpAddressFormatting)
{
uint8_t data[] = { 0x01, 0x02, 0x03, 0x04 };
MemoryDump dump { std::span<uint8_t>(data) };
StringPrintStream stream;
stream.print(dump);
auto result = stream.tryToString();
EXPECT_TRUE(result.has_value());
String output = result.value();
auto lines = output.split('\n');
EXPECT_EQ(lines.size(), 1u);
String dataLine = lines[0];
// Address should be 8 hex digits followed by ": "
auto colonPos = dataLine.find(':');
EXPECT_NE(colonPos, notFound);
String addressStr = dataLine.left(colonPos);
StringPrintStream expectedStream;
expectedStream.print(hex(reinterpret_cast<uintptr_t>(data), Lowercase));
auto expectedResult = expectedStream.tryToString();
EXPECT_TRUE(expectedResult.has_value());
String expected = expectedResult.value();
EXPECT_EQ(addressStr, expected);
}
TEST(WTF, MemoryDumpBasic)
{
uint8_t data[] = { 0x41, 0x42, 0x43, 0x44 }; // "ABCD"
// Test span constructor
std::span<uint8_t> dataSpan(data);
MemoryDump dump(dataSpan);
// Test that we can create a MemoryDump object
EXPECT_EQ(dump.span().data(), reinterpret_cast<const std::byte*>(data));
EXPECT_EQ(dump.span().size(), 4u);
// Test StringBuilder integration via StringPrintStream
StringPrintStream stream;
stream.print(dump);
auto expectedResult = stream.tryToString();
EXPECT_TRUE(expectedResult.has_value());
StringBuilder builder;
builder.append(expectedResult.value());
String result = builder.toString();
// Should contain some output
EXPECT_FALSE(result.isEmpty());
// Test default size limit
EXPECT_EQ(dump.sizeLimit(), MemoryDump::DefaultSizeLimit);
// Test custom size limit with span constructor
constexpr size_t customLimit = 512;
MemoryDump spanDump(dataSpan, customLimit);
EXPECT_EQ(spanDump.span().data(), reinterpret_cast<const std::byte*>(data));
EXPECT_EQ(spanDump.span().size(), 4u);
EXPECT_EQ(spanDump.sizeLimit(), customLimit);
// Test output with custom limit (should still work for small data)
StringPrintStream limitedStream;
limitedStream.print(spanDump);
auto limitedResult = limitedStream.tryToString();
EXPECT_TRUE(limitedResult.has_value());
EXPECT_FALSE(limitedResult.value().isEmpty());
}
TEST(WTF, MemoryDumpRange)
{
uint8_t data[] = { 0x41, 0x42, 0x43, 0x44, 0x45 }; // "ABCDE"
// Test range with pointers in correct order
MemoryDump dump1 = MemoryDump(std::span { data });
EXPECT_EQ(dump1.span().data(), reinterpret_cast<const std::byte*>(data));
EXPECT_EQ(dump1.span().size(), 5u);
EXPECT_EQ(dump1.sizeLimit(), MemoryDump::DefaultSizeLimit);
// Test range with custom size limit
constexpr size_t customLimit = 256;
MemoryDump dump3(std::span { data }.first(3), customLimit);
EXPECT_EQ(dump3.span().data(), reinterpret_cast<const std::byte*>(data));
EXPECT_EQ(dump3.span().size(), 3u);
EXPECT_EQ(dump3.sizeLimit(), customLimit);
// Test outputs
StringPrintStream stream1;
stream1.print(dump1);
auto result1 = stream1.tryToString();
EXPECT_TRUE(result1.has_value());
EXPECT_TRUE(result1.value().contains("ABCDE"_s));
}
} // namespace TestWebKitAPI