/* * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fud_algorithm.hpp" #include "fud_allocator.hpp" #include "fud_array.hpp" #include "fud_string.hpp" #include "fud_utf8.hpp" #include "fud_utf8_iterator.hpp" #include "fud_vector.hpp" #include "test_common.hpp" // #include "fud_format.hpp" // #include "fud_span.hpp" #include "gtest/gtest.h" namespace fud { using classify::CharPredicate; using classify::FudUtf8Predicate; using classify::isAscii; using classify::isAscii; using classify:: isAlphanumeric; using classify:: isAlphanumeric; using classify:: isAlpha; using classify:: isAlpha; using classify:: isLowercase; using classify:: isLowercase; using classify:: isUppercase; using classify:: isUppercase; using classify:: isDigit; using classify:: isDigit; using classify:: isHexDigit; using classify:: isHexDigit; using classify:: isControl; using classify:: isControl; using classify:: isGraphical; using classify:: isGraphical; using classify:: isSpace; using classify:: isSpace; using classify:: isBlank; using classify:: isBlank; using classify:: isPrintable; using classify:: isPrintable; using classify:: isPunctuation; using classify:: isPunctuation; constexpr size_t validAsciiSize = INT8_MAX + 1; constexpr size_t invalidAsciiSize = UINT8_MAX + 1 - validAsciiSize; constexpr size_t numControlChars = 33; constexpr char printableCharOffset = 0x20; constexpr auto invalidAscii = FudUtf8::invalidAsciiCode.character(); auto invalidAsciiGenerator() { return Iota{validAsciiSize, 1, invalidAsciiSize}; } template auto toUtf8(T letter) { return FudUtf8::make(static_cast(letter)); } template auto toLetter(T letter) { return static_cast(letter); } TEST(Utf8Test, Utf8Creation) { const Array threeByte = {THREE_BYTE}; FudUtf8 utf8Point{FudUtf8::make(threeByte)}; ASSERT_NE(utf8Point.data(), nullptr); ASSERT_EQ(utf8Point.size(), 3); ASSERT_NE(utf8Point.hash(), -1); const Array asciiLetter = {'A'}; utf8Point = FudUtf8::make(asciiLetter); ASSERT_NE(utf8Point.data(), nullptr); ASSERT_EQ(utf8Point.size(), 1); const Array twoByte = {TWO_BYTE}; utf8Point = FudUtf8::make(twoByte); ASSERT_NE(utf8Point.data(), nullptr); ASSERT_EQ(utf8Point.size(), 2); ASSERT_NE(utf8Point.hash(), -1); Array fourByte = { static_cast(FOUR_BYTE[0]), static_cast(FOUR_BYTE[1]), static_cast(FOUR_BYTE[2]), static_cast(FOUR_BYTE[3])}; utf8Point = FudUtf8::make(fourByte); ASSERT_NE(utf8Point.data(), nullptr); ASSERT_EQ(utf8Point.size(), 4); ASSERT_NE(utf8Point.hash(), -1); const Array invalidBytes = {0xFF, 0xFF, 0xFF, 0xFF}; utf8Point = FudUtf8::make(invalidBytes); ASSERT_EQ(utf8Point.data(), nullptr); ASSERT_EQ(utf8Point.size(), 0); ASSERT_EQ(utf8Point.hash(), -1); } TEST(Utf8Test, Utf8MultiByte) { Array data{MULTI_BYTE_LITERAL}; constexpr size_t bufSize = data.size(); EXPECT_EQ(data[bufSize - 1], '\0'); class FixedAllocator : public Allocator { private: Array m_memory{}; size_t m_allocated{0}; public: virtual ~FixedAllocator() override = default; virtual Result allocate(size_t bytes, size_t alignment) override final { using RetType = Result; static_cast(alignment); if (bytes > m_memory.size() - m_allocated) { return RetType::error(FudStatus::AllocFailure); } auto* data = m_memory.data() + m_allocated; m_allocated += bytes; return RetType::okay(data); } virtual void deallocate(std::byte* pointer, size_t bytes) override final { static_cast(pointer); static_cast(bytes); } virtual bool isEqual(const Allocator& rhs) const override final { return &rhs == this; } }; FixedAllocator fixedAllocator; auto stringBufferRes{String::makeFromCString(MULTI_BYTE_LITERAL, &fixedAllocator)}; ASSERT_TRUE(stringBufferRes.isOkay()); auto stringBuffer{stringBufferRes.takeOkay()}; EXPECT_EQ(stringBuffer.size(), bufSize); EXPECT_EQ(stringBuffer.size(), sizeof(data)); EXPECT_EQ(stringBuffer.length(), bufSize - 1); ASSERT_TRUE(stringBuffer.utf8Valid()); Utf8Iterator utf8Iter{stringBuffer}; auto characterOpt = utf8Iter.next(); ASSERT_TRUE(characterOpt.hasValue()); // MULTI_BYTE_LITERAL "test今日素敵はですねƩ®😀z" const Array multiByteCharacters{ FudUtf8::make(Utf8Variant{Ascii{'t'}}), FudUtf8::make(Utf8Variant{Ascii{'e'}}), FudUtf8::make(Utf8Variant{Ascii{'s'}}), FudUtf8::make(Utf8Variant{Ascii{'t'}}), FudUtf8::from(StringView{sizeof("今"), "今"}, 0), FudUtf8::from(StringView{sizeof("日"), "日"}, 0), FudUtf8::from(StringView{sizeof("素"), "素"}, 0), FudUtf8::from(StringView{sizeof("敵"), "敵"}, 0), FudUtf8::from(StringView{sizeof("は"), "は"}, 0), FudUtf8::from(StringView{sizeof("で"), "で"}, 0), FudUtf8::from(StringView{sizeof("す"), "す"}, 0), FudUtf8::from(StringView{sizeof("ね"), "ね"}, 0), FudUtf8::from(StringView{sizeof("Ʃ"), "Ʃ"}, 0), FudUtf8::from(StringView{sizeof("®"), "®"}, 0), FudUtf8::from(StringView{sizeof("😀"), "😀"}, 0), FudUtf8::make(Utf8Variant{Ascii{'z'}}), }; size_t idx = 0; while (characterOpt.hasValue()) { auto character = characterOpt.value(); if (character != FudUtf8{Utf8Variant{Ascii{'\0'}}}) { EXPECT_TRUE(character.size() >= 1); ASSERT_LT(idx, multiByteCharacters.size()); EXPECT_EQ(character.size(), multiByteCharacters[idx].size()); EXPECT_EQ(character, multiByteCharacters[idx]); EXPECT_TRUE(multiByteCharacters[idx].valid()); idx++; } characterOpt = utf8Iter.next(); } utf8Iter.reset(); ASSERT_TRUE(utf8Iter.next().hasValue()); FudUtf8 invalid = FudUtf8::invalidAscii(); ASSERT_FALSE(invalid.valid()); ASSERT_EQ(invalid.size(), 0); ASSERT_EQ(invalid.data(), nullptr); ASSERT_EQ(invalid.hash(), -1); } TEST(Utf8Test, Utf8IsAscii) { ASSERT_FALSE(isAscii(invalidAscii)); Iota charIota{0, 1, validAsciiSize}; ASSERT_TRUE(allOf([&]() -> Option { return charIota().map(toLetter); }, static_cast(isAscii))); Iota invalidCharIota{validAsciiSize, 1, invalidAsciiSize}; ASSERT_FALSE(anyOf([&]() -> Option { return invalidCharIota().map(toLetter); }, static_cast(isAscii))); FudUtf8 unicode{FudUtf8::invalidAscii()}; ASSERT_FALSE(isAscii(unicode)); charIota.set(0); ASSERT_TRUE(allOf([&]() -> Option { return charIota().map(toUtf8); }, static_cast(isAscii))); invalidCharIota.set(invalidAsciiSize); ASSERT_FALSE(anyOf([&]() -> Option { return invalidCharIota().map(toUtf8); }, static_cast(isAscii))); } template struct SpanGenerator { Span span; size_t index{0}; void reset() { index = 0; } Option operator()() { if (index < span.size()) { index++; return span[index - 1]; } return NullOpt; } }; TEST(Utf8Test, Utf8IsAlphanumeric) { constexpr size_t numAlphanumericChars = 26 * 2 + 10; Array alphanumericCharLiteral{ALPHA_NUMERIC_CHARS}; Array alphanumericChars{}; copyMem(alphanumericChars, alphanumericCharLiteral); ASSERT_TRUE(allOf(alphanumericChars.span(), static_cast(isAlphanumeric))); constexpr size_t numNonAlphanumericChars = validAsciiSize - numAlphanumericChars; Vector nonAlphanumericChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { if (!isAlphanumeric(idx)) { ASSERT_EQ(nonAlphanumericChars.pushBack(idx), FudStatus::Success); } } auto nonAlphanumericSpan{nonAlphanumericChars.span().takeOkay()}; ASSERT_FALSE(anyOf(nonAlphanumericSpan, static_cast(isAlphanumeric))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isAlphanumeric))); ASSERT_FALSE(isAlphanumeric(FudUtf8{Ascii{invalidAscii}})); auto iotaGenerator = invalidAsciiGenerator(); auto generator = generate( []() { return Array{}; }, [&]() { return iotaGenerator().map([](auto val) { return static_cast(val); }); }); SpanGenerator alphanumericGenerator{alphanumericChars.span()}; auto utf8AlphanumericGenerator = [&]() { return alphanumericGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8AlphanumericGenerator, static_cast(isAlphanumeric))); SpanGenerator nonAlphanumericGenerator{nonAlphanumericChars.span().takeOkay()}; auto utf8NonAlphanumericGenerator = [&]() { return nonAlphanumericGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonAlphanumericGenerator, static_cast(isAlphanumeric))); } TEST(Utf8Test, Utf8IsAlpha) { constexpr size_t numAlphaChars = sizeof(ALPHA_CHARS) - 1; Array alphaCharLiteral{ALPHA_CHARS}; Array alphaChars{}; copyMem(alphaChars, alphaCharLiteral); ASSERT_TRUE(allOf(alphaChars.span(), static_cast(isAlpha))); constexpr size_t numNonAlphanumericChars = validAsciiSize - numAlphaChars; Vector nonAlphaChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { if (!isAlphanumeric(idx)) { ASSERT_EQ(nonAlphaChars.pushBack(idx), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonAlphaChars.span().takeOkay(), static_cast(isAlpha))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isAlpha))); ASSERT_FALSE(isAlpha(FudUtf8{Ascii{invalidAscii}})); SpanGenerator alphaGenerator{alphaChars.span()}; auto utf8AlphaGenerator = [&]() { return alphaGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8AlphaGenerator, static_cast(isAlpha))); SpanGenerator nonAlphaGenerator{nonAlphaChars.span().takeOkay()}; auto utf8NonAlphaGenerator = [&]() { return nonAlphaGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonAlphaGenerator, static_cast(isAlpha))); } TEST(Utf8Test, Utf8IsLower) { constexpr size_t numLowerChars = 26; Array lowerCharLiteral{LOWERCASE_CHARS}; Array lowerChars{}; copyMem(lowerChars, lowerCharLiteral); ASSERT_TRUE(allOf(lowerChars.span(), static_cast(isLowercase))); constexpr size_t numNonLowerChars = validAsciiSize - numLowerChars; Vector nonLowerChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { if (!isLowercase(idx)) { ASSERT_EQ(nonLowerChars.pushBack(idx), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonLowerChars.span().takeOkay(), static_cast(isLowercase))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isLowercase))); ASSERT_FALSE(isLowercase(FudUtf8{Ascii{invalidAscii}})); SpanGenerator lowerGenerator{lowerChars.span()}; auto utf8LowerGenerator = [&]() { return lowerGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8LowerGenerator, static_cast(isLowercase))); SpanGenerator nonLowerGenerator{nonLowerChars.span().takeOkay()}; auto utf8NonLowerGenerator = [&]() { return nonLowerGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonLowerGenerator, static_cast(isLowercase))); } TEST(Utf8Test, Utf8IsUpper) { constexpr size_t numUpperChars = 26; Array upperCharLiteral{UPPERCASE_CHARS}; Array upperChars{}; copyMem(upperChars, upperCharLiteral); ASSERT_TRUE(allOf(upperChars.span(), static_cast(isUppercase))); constexpr size_t numNonUpperChars = validAsciiSize - numUpperChars; Vector nonUpperChars{}; for (utf8 idx = 0; idx < INT8_MAX; ++idx) { if (!isUppercase(idx)) { ASSERT_EQ(nonUpperChars.pushBack(idx), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonUpperChars.span().takeOkay(), static_cast(isUppercase))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isUppercase))); ASSERT_FALSE(isUppercase(FudUtf8{Ascii{invalidAscii}})); SpanGenerator upperGenerator{upperChars.span()}; auto utf8UpperGenerator = [&]() { return upperGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8UpperGenerator, static_cast(isUppercase))); SpanGenerator nonUpperGenerator{nonUpperChars.span().takeOkay()}; auto utf8NonUpperGenerator = [&]() { return nonUpperGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonUpperGenerator, static_cast(isUppercase))); } TEST(Utf8Test, Utf8IsDigit) { constexpr size_t numDigitChars = 10; Array digitCharLiteral{DECIMAL_CHARS}; Array digitChars{}; copyMem(digitChars, digitCharLiteral); ASSERT_TRUE(allOf(digitChars.span(), static_cast(isDigit))); constexpr size_t numNonDigitChars = validAsciiSize - numDigitChars; Vector nonDigitChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { if (!isDigit(idx)) { ASSERT_EQ(nonDigitChars.pushBack(idx), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonDigitChars.span().takeOkay(), static_cast(isDigit))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isDigit))); ASSERT_FALSE(isDigit(FudUtf8{Ascii{invalidAscii}})); SpanGenerator digitGenerator{digitChars.span()}; auto utf8DigitGenerator = [&]() { return digitGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8DigitGenerator, static_cast(isDigit))); SpanGenerator nonDigitGenerator{nonDigitChars.span().takeOkay()}; auto utf8NonDigitGenerator = [&]() { return nonDigitGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonDigitGenerator, static_cast(isDigit))); } TEST(Utf8Test, Utf8IsHexDigit) { constexpr size_t numHexDigitChars = 6 * 2 + 10; Array hexDigitCharLiteral{u8"abcdefABCDEF0123456789"}; Array hexDigitChars{}; copyMem(hexDigitChars, hexDigitCharLiteral); ASSERT_TRUE(allOf(hexDigitChars.span(), static_cast(isHexDigit))); constexpr size_t numNonHexDigitChars = validAsciiSize - numHexDigitChars; Vector nonHexDigitChars{}; for (char idx = 0; idx < INT8_MAX; ++idx) { if (!isHexDigit(idx)) { ASSERT_EQ(nonHexDigitChars.pushBack(idx), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonHexDigitChars.span().takeOkay(), static_cast(isHexDigit))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isHexDigit))); ASSERT_FALSE(isHexDigit(FudUtf8{Ascii{invalidAscii}})); SpanGenerator hexDigitGenerator{hexDigitChars.span()}; auto utf8HexDigitGenerator = [&]() { return hexDigitGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8HexDigitGenerator, static_cast(isHexDigit))); SpanGenerator nonHexDigitGenerator{nonHexDigitChars.span().takeOkay()}; auto utf8NonHexDigitGenerator = [&]() { return nonHexDigitGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonHexDigitGenerator, static_cast(isHexDigit))); } TEST(Utf8Test, Utf8IsControl) { Iota controlArrayGenerator{0, 1, numControlChars}; auto controlChars = generate([]() { return Array{}; }, controlArrayGenerator); constexpr const char deleteChar = 0x7F; controlChars.back() = deleteChar; ASSERT_TRUE(allOf(controlChars.span(), static_cast(isControl))); constexpr size_t numNonControlChars = INT8_MAX + 1 - numControlChars; Vector nonControlChars{}; ASSERT_EQ(nonControlChars.reserve(numNonControlChars), FudStatus::Success); for (auto idx = numControlChars - 1; idx < deleteChar; ++idx) { ASSERT_EQ(nonControlChars.pushBack(idx), FudStatus::Success); } ASSERT_FALSE(anyOf(nonControlChars.span().takeOkay(), static_cast(isControl))); ASSERT_TRUE(allOf(nonControlChars.span().takeOkay(), static_cast(isAscii))); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isControl))); ASSERT_FALSE(isControl(FudUtf8{Ascii{invalidAscii}})); SpanGenerator controlGenerator{controlChars.span()}; auto utf8ControlGenerator = [&]() { return controlGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8ControlGenerator, static_cast(isControl))); SpanGenerator nonControlGenerator{nonControlChars.span().takeOkay()}; auto utf8NonControlGenerator = [&]() { return nonControlGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonControlGenerator, static_cast(isControl))); } TEST(Utf8Test, Utf8IsGraphical) { constexpr size_t numGraphicalChars = sizeof(GRAPHICAL_CHARS) - 1; Array graphicalCharLiteral{GRAPHICAL_CHARS}; Array graphicalChars{}; copyMem(graphicalChars, graphicalCharLiteral); ASSERT_TRUE(allOf(graphicalChars.span(), static_cast(isGraphical))); constexpr size_t numNonGraphicalChars = validAsciiSize - numGraphicalChars; Vector nonGraphicalChars{}; ASSERT_EQ(nonGraphicalChars.reserve(numNonGraphicalChars), FudStatus::Success); for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { if (!isGraphical(static_cast(idx))) { ASSERT_EQ(nonGraphicalChars.pushBack(static_cast(idx)), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonGraphicalChars.span().takeOkay(), static_cast(isGraphical))); ASSERT_TRUE(allOf(nonGraphicalChars.span().takeOkay(), static_cast(isAscii))); ASSERT_EQ(nonGraphicalChars.size() + graphicalChars.size(), INT8_MAX + 1); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isGraphical))); ASSERT_FALSE(isGraphical(FudUtf8{Ascii{invalidAscii}})); SpanGenerator graphicalGenerator{graphicalChars.span()}; auto utf8GraphicalGenerator = [&]() { return graphicalGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8GraphicalGenerator, static_cast(isGraphical))); SpanGenerator nonGraphicalGenerator{nonGraphicalChars.span().takeOkay()}; auto utf8NonGraphicalGenerator = [&]() { return nonGraphicalGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonGraphicalGenerator, static_cast(isGraphical))); } TEST(Utf8Test, Utf8IsSpace) { constexpr size_t numSpaceChars = sizeof(SPACE_CHARS) - 1; Array spaceCharLiteral{SPACE_CHARS}; Array spaceChars{}; copyMem(spaceChars, spaceCharLiteral); ASSERT_TRUE(allOf(spaceChars.span(), static_cast(isSpace))); constexpr size_t numNonSpaceChars = validAsciiSize - numSpaceChars; Vector nonSpaceChars{}; ASSERT_EQ(nonSpaceChars.reserve(numNonSpaceChars), FudStatus::Success); for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { if (!isSpace(static_cast(idx))) { ASSERT_EQ(nonSpaceChars.pushBack(static_cast(idx)), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonSpaceChars.span().takeOkay(), static_cast(isSpace))); ASSERT_TRUE(allOf(nonSpaceChars.span().takeOkay(), static_cast(isAscii))); ASSERT_EQ(nonSpaceChars.size() + spaceChars.size(), INT8_MAX + 1); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isSpace))); ASSERT_FALSE(isSpace(FudUtf8{Ascii{invalidAscii}})); SpanGenerator spaceGenerator{spaceChars.span()}; auto utf8SpaceGenerator = [&]() { return spaceGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8SpaceGenerator, static_cast(isSpace))); SpanGenerator nonSpaceGenerator{nonSpaceChars.span().takeOkay()}; auto utf8NonSpaceGenerator = [&]() { return nonSpaceGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonSpaceGenerator, static_cast(isSpace))); } TEST(Utf8Test, Utf8IsBlank) { constexpr size_t numBlankChars = sizeof(BLANK_CHARS) - 1; Array blankCharLiteral{BLANK_CHARS}; Array blankChars{}; copyMem(blankChars, blankCharLiteral); ASSERT_TRUE(allOf(blankChars.span(), static_cast(isBlank))); constexpr size_t numNonBlankChars = validAsciiSize - numBlankChars; Vector nonBlankChars{}; ASSERT_EQ(nonBlankChars.reserve(numNonBlankChars), FudStatus::Success); for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { if (!isBlank(static_cast(idx))) { ASSERT_EQ(nonBlankChars.pushBack(static_cast(idx)), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonBlankChars.span().takeOkay(), static_cast(isBlank))); ASSERT_TRUE(allOf(nonBlankChars.span().takeOkay(), static_cast(isAscii))); ASSERT_EQ(nonBlankChars.size() + blankChars.size(), INT8_MAX + 1); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isBlank))); ASSERT_FALSE(isBlank(FudUtf8{Ascii{invalidAscii}})); SpanGenerator blankGenerator{blankChars.span()}; auto utf8BlankGenerator = [&]() { return blankGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8BlankGenerator, static_cast(isBlank))); SpanGenerator nonBlankGenerator{nonBlankChars.span().takeOkay()}; auto utf8NonBlankGenerator = [&]() { return nonBlankGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonBlankGenerator, static_cast(isBlank))); } TEST(Utf8Test, Utf8IsPrintable) { constexpr size_t numPrintableChars = sizeof(PRINTABLE_CHARS) - 1; Array printableCharLiteral{PRINTABLE_CHARS}; Array printableChars{}; copyMem(printableChars, printableCharLiteral); ASSERT_TRUE(allOf(printableChars.span(), static_cast(isPrintable))); constexpr size_t numNonPrintableChars = validAsciiSize - numPrintableChars; Vector nonPrintableChars{}; ASSERT_EQ(nonPrintableChars.reserve(numNonPrintableChars), FudStatus::Success); for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { if (!isPrintable(static_cast(idx))) { ASSERT_EQ(nonPrintableChars.pushBack(static_cast(idx)), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonPrintableChars.span().takeOkay(), static_cast(isPrintable))); ASSERT_TRUE(allOf(nonPrintableChars.span().takeOkay(), static_cast(isAscii))); ASSERT_EQ(nonPrintableChars.size() + printableChars.size(), INT8_MAX + 1); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isPrintable))); ASSERT_FALSE(isPrintable(FudUtf8{Ascii{invalidAscii}})); SpanGenerator printableGenerator{printableChars.span()}; auto utf8PrintableGenerator = [&]() { return printableGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8PrintableGenerator, static_cast(isPrintable))); SpanGenerator nonPrintableGenerator{nonPrintableChars.span().takeOkay()}; auto utf8NonPrintableGenerator = [&]() { return nonPrintableGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonPrintableGenerator, static_cast(isPrintable))); } TEST(Utf8Test, Utf8IsPunctuation) { constexpr size_t numPunctuationChars = sizeof(PUNCTUATION_CHARS) - 1; Array punctuationCharLiteral{PUNCTUATION_CHARS}; Array punctuationChars{}; copyMem(punctuationChars, punctuationCharLiteral); ASSERT_TRUE(allOf(punctuationChars.span(), static_cast(isPunctuation))); constexpr size_t numNonPunctuationChars = validAsciiSize - numPunctuationChars; Vector nonPunctuationChars{}; ASSERT_EQ(nonPunctuationChars.reserve(numNonPunctuationChars), FudStatus::Success); for (uint8_t idx = 0; idx < INT8_MAX + 1; ++idx) { if (!isPunctuation(static_cast(idx))) { ASSERT_EQ(nonPunctuationChars.pushBack(static_cast(idx)), FudStatus::Success); } } ASSERT_FALSE(anyOf(nonPunctuationChars.span().takeOkay(), static_cast(isPunctuation))); ASSERT_TRUE(allOf(nonPunctuationChars.span().takeOkay(), static_cast(isAscii))); ASSERT_EQ(nonPunctuationChars.size() + punctuationChars.size(), INT8_MAX + 1); auto invalidAsciiChars = invalidAsciiGenerator(); ASSERT_FALSE(anyOf(invalidAsciiChars, static_cast(isPunctuation))); ASSERT_FALSE(isPunctuation(FudUtf8{Ascii{invalidAscii}})); SpanGenerator punctuationGenerator{punctuationChars.span()}; auto utf8PunctuationGenerator = [&]() { return punctuationGenerator().map(toUtf8); }; ASSERT_TRUE(allOf(utf8PunctuationGenerator, static_cast(isPunctuation))); SpanGenerator nonPunctuationGenerator{nonPunctuationChars.span().takeOkay()}; auto utf8NonPunctuationGenerator = [&]() { return nonPunctuationGenerator().map(toUtf8); }; ASSERT_FALSE(anyOf(utf8NonPunctuationGenerator, static_cast(isPunctuation))); } } // namespace fud