blob: 0747c874d4f68b73988b7a7d5ae5dd6a27f80520 [file]
/*
* 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 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 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 "Helpers/Test.h"
#include <CoreFoundation/CoreFoundation.h>
#include <wtf/RetainPtr.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
namespace TestWebKitAPI {
TEST(StringCF, ConstructFromNull)
{
String string(static_cast<CFStringRef>(nullptr));
EXPECT_TRUE(string.isNull());
}
TEST(StringCF, ConstructFromEmpty)
{
String string(CFSTR(""));
EXPECT_FALSE(string.isNull());
EXPECT_TRUE(string.isEmpty());
EXPECT_TRUE(string.is8Bit());
}
TEST(StringCF, ConstructFromLatin1)
{
String string(CFSTR("Hello, world!"));
EXPECT_EQ(string.length(), 13u);
EXPECT_TRUE(string.is8Bit());
EXPECT_EQ(string, "Hello, world!"_s);
}
TEST(StringCF, ConstructFromLatin1WithHighBytes)
{
// Create a CFString with Latin-1 characters above ASCII range (e.g. U+00E9 = é).
const uint8_t latin1Bytes[] = { 'r', 0xE9, 's', 'u', 'm', 0xE9 }; // "résumé" in Latin-1
RetainPtr cfString = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, latin1Bytes, sizeof(latin1Bytes), kCFStringEncodingISOLatin1, false));
String string(cfString.get());
EXPECT_EQ(string.length(), 6u);
EXPECT_FALSE(string.isNull());
EXPECT_EQ(string, String::fromUTF8("résumé"));
}
TEST(StringCF, ConstructFromUTF16WithLatin1Characters)
{
// Create a long CFString from UTF-16 characters that are all in the Latin-1 range.
// Use a long string so CF is likely to expose its internal UTF-16 buffer via
// CFStringGetCharactersSpan, exercising the create8BitIfPossible narrowing path.
Vector<char16_t> characters(FillWith { }, 4096, 'A');
RetainPtr cfString = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(characters.span().data()), characters.size()));
String string(cfString.get());
EXPECT_EQ(string.length(), 4096u);
EXPECT_TRUE(string.is8Bit());
EXPECT_EQ(string, String(Vector<Latin1Character>(FillWith { }, 4096, 'A')));
}
TEST(StringCF, ConstructFromUTF16WithHighLatin1Characters)
{
// Create a CFString from UTF-16 characters that include high Latin-1 chars (U+00E9).
const char16_t characters[] = { 'r', 0x00E9, 's', 'u', 'm', 0x00E9 }; // "résumé"
RetainPtr cfString = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(characters), std::size(characters)));
String string(cfString.get());
EXPECT_EQ(string.length(), 6u);
EXPECT_FALSE(string.isNull());
EXPECT_EQ(string, String::fromUTF8("résumé"));
}
TEST(StringCF, ConstructFromUTF16WithNonLatin1Characters)
{
// Create a CFString with characters outside Latin-1 range (e.g. U+4E16 = 世, U+754C = 界).
const char16_t characters[] = { 0x4E16, 0x754C }; // "世界"
RetainPtr cfString = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(characters), std::size(characters)));
String string(cfString.get());
EXPECT_EQ(string.length(), 2u);
EXPECT_FALSE(string.is8Bit());
EXPECT_EQ(string[0], 0x4E16);
EXPECT_EQ(string[1], 0x754C);
}
TEST(StringCF, ConstructFromUTF16MixedLatin1AndNonLatin1)
{
// Mix of Latin-1 and non-Latin-1 characters — should stay 16-bit.
const char16_t characters[] = { 'H', 'i', 0x4E16 }; // "Hi世"
RetainPtr cfString = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(characters), std::size(characters)));
String string(cfString.get());
EXPECT_EQ(string.length(), 3u);
EXPECT_FALSE(string.is8Bit());
EXPECT_EQ(string[0], 'H');
EXPECT_EQ(string[1], 'i');
EXPECT_EQ(string[2], 0x4E16);
}
TEST(StringCF, RoundTrip)
{
String original("WebKit"_s);
auto cfString = original.createCFString();
String roundTripped(cfString.get());
EXPECT_EQ(original, roundTripped);
EXPECT_TRUE(roundTripped.is8Bit());
}
} // namespace TestWebKitAPI