TM-SGNL-iOS/SignalServiceKit/tests/Util/StringTest.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

365 lines
25 KiB
Swift
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import XCTest
import SignalServiceKit
final class StringTest: XCTestCase {
func test_digitsOnly() {
XCTAssertEqual("".digitsOnly(), "")
XCTAssertEqual("abc".digitsOnly(), "")
XCTAssertEqual("123".digitsOnly(), "123")
XCTAssertEqual("-1.23".digitsOnly(), "123")
XCTAssertEqual("1x2 3".digitsOnly(), "123")
XCTAssertEqual("١23".digitsOnly(), "١23")
XCTAssertEqual("1⃣23".digitsOnly(), "123")
}
func test_asciiDigitsOnly() {
XCTAssertEqual("".asciiDigitsOnly, "")
XCTAssertEqual("abc".asciiDigitsOnly, "")
XCTAssertEqual("123".asciiDigitsOnly, "123")
XCTAssertEqual("-1.23".asciiDigitsOnly, "123")
XCTAssertEqual("1x2 3".asciiDigitsOnly, "123")
XCTAssertEqual("1١23".asciiDigitsOnly, "123")
XCTAssertEqual("19⃣23".asciiDigitsOnly, "123")
XCTAssertEqual("6⃣123".asciiDigitsOnly, "123")
}
func test_isAsciiDigitsOnly() throws {
XCTAssertTrue("".isAsciiDigitsOnly)
XCTAssertTrue("1".isAsciiDigitsOnly)
XCTAssertTrue("1234567890".isAsciiDigitsOnly)
XCTAssertFalse(" ".isAsciiDigitsOnly)
XCTAssertFalse("x".isAsciiDigitsOnly)
XCTAssertFalse("x1".isAsciiDigitsOnly)
XCTAssertFalse("1x".isAsciiDigitsOnly)
XCTAssertFalse("1.2".isAsciiDigitsOnly)
XCTAssertFalse("1".isAsciiDigitsOnly)
XCTAssertFalse("١٢٣".isAsciiDigitsOnly)
}
func test_caesar() {
XCTAssertEqual("abc", try! "abc".caesar(shift: 0))
XCTAssertEqual("abc", try! "abc".caesar(shift: 127))
XCTAssertEqual("bcd", try! "abc".caesar(shift: 1))
XCTAssertEqual("bcd", try! "abc".caesar(shift: 128))
XCTAssertEqual("z{b", try! "yza".caesar(shift: 1))
XCTAssertEqual("|}d", try! "yza".caesar(shift: 3))
XCTAssertEqual("ef=g", try! "bc:d".caesar(shift: 3))
let shifted = try! "abc".caesar(shift: 32)
let roundTrip = try! shifted.caesar(shift: 127 - 32)
XCTAssertEqual("abc", roundTrip)
}
func test_encodedForSelector() {
XCTAssertEqual("cnN0", "abc".encodedForSelector)
XCTAssertEqual("abc", "abc".encodedForSelector!.decodedForSelector)
XCTAssertNotEqual("abcWithFoo:bar:", "abcWithFoo:bar:".encodedForSelector)
XCTAssertEqual("abcWithFoo:bar:", "abcWithFoo:bar:".encodedForSelector!.decodedForSelector)
XCTAssertNotEqual("abcWithFoo:bar:zaz1:", "abcWithFoo:bar:zaz1:".encodedForSelector)
XCTAssertEqual("abcWithFoo:bar:zaz1:", "abcWithFoo:bar:zaz1:".encodedForSelector!.decodedForSelector)
}
func test_directionalAppend() {
// We used to have a rtlSafeAppend helper, but it didn't behave quite like expected
// because iOS tries to be smart about the language of the string you're appending to.
//
// Sanity check that the iOS methods are doing what we want.
// Basic tests, "a" + "b" = "ab", etc.
XCTAssertEqual("a" + "b", "ab")
XCTAssertEqual("hello" + " " + "world", "hello world")
XCTAssertEqual("a" + " " + "1" + " " + "b", "a 1 b")
XCTAssertEqual("ا" + "ب", "اب")
XCTAssertEqual("مرحبا" + " " + "بالعالم", "مرحبا بالعالم")
XCTAssertEqual("ا" + " " + "1" + " " + "ب", "ا 1 ب")
// Test a common usage, similar to `formatPastTimestampRelativeToNow` where we append a time to a date.
let testTime = "9:41"
let testStrings: [(day: String, expectedConcatentation: String)] = [
// LTR Tests
("Today", "Today 9:41"), // English
("Heute", "Heute 9:41"), // German
// RTL Tests
("اليوم", "اليوم 9:41"), // Arabic
("היום", "היום 9:41") // Hebrew
]
for (day, expectedConcatentation) in testStrings {
XCTAssertEqual(day + " " + testTime, expectedConcatentation)
XCTAssertEqual((day).appending(" ").appending(testTime), expectedConcatentation)
XCTAssertEqual(NSAttributedString(string: day) + " " + testTime, NSAttributedString(string: expectedConcatentation))
}
}
func test_formatDurationLossless() {
let secondsPerMinute: UInt32 = 60
let secondsPerHour: UInt32 = secondsPerMinute * 60
let secondsPerDay: UInt32 = secondsPerHour * 24
let secondsPerWeek: UInt32 = secondsPerDay * 7
let secondsPerYear: UInt32 = secondsPerDay * 365
let format: (UInt32) -> String = String.formatDurationLossless
XCTAssertEqual(format(0), "0 seconds")
XCTAssertEqual(format(1), "1 second")
XCTAssertEqual(format(2), "2 seconds")
XCTAssertEqual(format(1 * secondsPerMinute - 1), "59 seconds")
XCTAssertEqual(format(1 * secondsPerMinute), "1 minute")
XCTAssertEqual(format(1 * secondsPerMinute + 1), "1 minute, 1 second")
XCTAssertEqual(format(1 * secondsPerMinute + 2), "1 minute, 2 seconds")
XCTAssertEqual(format(2 * secondsPerMinute - 1), "1 minute, 59 seconds")
XCTAssertEqual(format(2 * secondsPerMinute), "2 minutes")
XCTAssertEqual(format(2 * secondsPerMinute + 1), "2 minutes, 1 second")
XCTAssertEqual(format(2 * secondsPerMinute + 2), "2 minutes, 2 seconds")
XCTAssertEqual(format(1 * secondsPerHour - 1), "59 minutes, 59 seconds")
XCTAssertEqual(format(1 * secondsPerHour), "1 hour")
XCTAssertEqual(format(1 * secondsPerHour + 1), "1 hour, 1 second")
XCTAssertEqual(format(1 * secondsPerHour + 2), "1 hour, 2 seconds")
XCTAssertEqual(format(1 * secondsPerHour + 1 * secondsPerMinute + 1), "1 hour, 1 minute, 1 second")
XCTAssertEqual(format(1 * secondsPerHour + 1 * secondsPerMinute + 2), "1 hour, 1 minute, 2 seconds")
XCTAssertEqual(format(1 * secondsPerHour + 2 * secondsPerMinute + 1), "1 hour, 2 minutes, 1 second")
XCTAssertEqual(format(1 * secondsPerHour + 2 * secondsPerMinute + 2), "1 hour, 2 minutes, 2 seconds")
XCTAssertEqual(format(2 * secondsPerHour - 1), "1 hour, 59 minutes, 59 seconds")
XCTAssertEqual(format(2 * secondsPerHour), "2 hours")
XCTAssertEqual(format(2 * secondsPerHour + 1), "2 hours, 1 second")
XCTAssertEqual(format(2 * secondsPerHour + 2), "2 hours, 2 seconds")
XCTAssertEqual(format(2 * secondsPerHour + 1 * secondsPerMinute + 1), "2 hours, 1 minute, 1 second")
XCTAssertEqual(format(2 * secondsPerHour + 1 * secondsPerMinute + 2), "2 hours, 1 minute, 2 seconds")
XCTAssertEqual(format(2 * secondsPerHour + 2 * secondsPerMinute + 1), "2 hours, 2 minutes, 1 second")
XCTAssertEqual(format(2 * secondsPerHour + 2 * secondsPerMinute + 2), "2 hours, 2 minutes, 2 seconds")
XCTAssertEqual(format(1 * secondsPerDay - 1), "23 hours, 59 minutes, 59 seconds")
XCTAssertEqual(format(1 * secondsPerDay), "1 day")
XCTAssertEqual(format(1 * secondsPerDay + 1), "1 day, 1 second")
XCTAssertEqual(format(1 * secondsPerDay + 2), "1 day, 2 seconds")
XCTAssertEqual(format(1 * secondsPerDay + 1 * secondsPerHour + 1), "1 day, 1 hour, 1 second")
XCTAssertEqual(format(1 * secondsPerDay + 1 * secondsPerHour + 2), "1 day, 1 hour, 2 seconds")
XCTAssertEqual(format(1 * secondsPerDay + 2 * secondsPerHour + 1), "1 day, 2 hours, 1 second")
XCTAssertEqual(format(1 * secondsPerDay + 2 * secondsPerHour + 2), "1 day, 2 hours, 2 seconds")
XCTAssertEqual(format(2 * secondsPerDay - 1), "1 day, 23 hours, 59 minutes, 59 seconds")
XCTAssertEqual(format(2 * secondsPerDay), "2 days")
XCTAssertEqual(format(2 * secondsPerDay + 1), "2 days, 1 second")
XCTAssertEqual(format(2 * secondsPerDay + 2), "2 days, 2 seconds")
XCTAssertEqual(format(2 * secondsPerDay + 1 * secondsPerHour + 1), "2 days, 1 hour, 1 second")
XCTAssertEqual(format(2 * secondsPerDay + 1 * secondsPerHour + 2), "2 days, 1 hour, 2 seconds")
XCTAssertEqual(format(2 * secondsPerDay + 2 * secondsPerHour + 1), "2 days, 2 hours, 1 second")
XCTAssertEqual(format(2 * secondsPerDay + 2 * secondsPerHour + 2), "2 days, 2 hours, 2 seconds")
XCTAssertEqual(format(1 * secondsPerWeek - 1), "6 days, 23 hours, 59 minutes, 59 seconds")
XCTAssertEqual(format(1 * secondsPerWeek), "1 week")
XCTAssertEqual(format(1 * secondsPerWeek + 1), "1 week, 1 second")
XCTAssertEqual(format(1 * secondsPerWeek + 2), "1 week, 2 seconds")
XCTAssertEqual(format(1 * secondsPerWeek + 1 * secondsPerDay + 1), "1 week, 1 day, 1 second")
XCTAssertEqual(format(1 * secondsPerWeek + 1 * secondsPerDay + 2), "1 week, 1 day, 2 seconds")
XCTAssertEqual(format(1 * secondsPerWeek + 2 * secondsPerDay + 1), "1 week, 2 days, 1 second")
XCTAssertEqual(format(1 * secondsPerWeek + 2 * secondsPerDay + 2), "1 week, 2 days, 2 seconds")
XCTAssertEqual(format(2 * secondsPerWeek - 1), "1 week, 6 days, 23 hours, 59 minutes, 59 seconds")
XCTAssertEqual(format(2 * secondsPerWeek), "2 weeks")
XCTAssertEqual(format(2 * secondsPerWeek + 1), "2 weeks, 1 second")
XCTAssertEqual(format(2 * secondsPerWeek + 2), "2 weeks, 2 seconds")
XCTAssertEqual(format(2 * secondsPerWeek + 1 * secondsPerDay + 1), "2 weeks, 1 day, 1 second")
XCTAssertEqual(format(2 * secondsPerWeek + 1 * secondsPerDay + 2), "2 weeks, 1 day, 2 seconds")
XCTAssertEqual(format(2 * secondsPerWeek + 2 * secondsPerDay + 1), "2 weeks, 2 days, 1 second")
XCTAssertEqual(format(2 * secondsPerWeek + 2 * secondsPerDay + 2), "2 weeks, 2 days, 2 seconds")
XCTAssertEqual(format(1 * secondsPerYear - 1), "52 weeks, 23 hours, 59 minutes, 59 seconds")
XCTAssertEqual(format(1 * secondsPerYear), "1 year")
XCTAssertEqual(format(1 * secondsPerYear + 1), "1 year, 1 second")
XCTAssertEqual(format(1 * secondsPerYear + 2), "1 year, 2 seconds")
XCTAssertEqual(format(1 * secondsPerYear + 1 * secondsPerWeek + 1), "1 year, 1 week, 1 second")
XCTAssertEqual(format(1 * secondsPerYear + 1 * secondsPerWeek + 2), "1 year, 1 week, 2 seconds")
XCTAssertEqual(format(1 * secondsPerYear + 2 * secondsPerWeek + 1), "1 year, 2 weeks, 1 second")
XCTAssertEqual(format(1 * secondsPerYear + 2 * secondsPerWeek + 2), "1 year, 2 weeks, 2 seconds")
XCTAssertEqual(format(2 * secondsPerYear - 1), "1 year, 52 weeks, 23 hours, 59 minutes, 59 seconds")
XCTAssertEqual(format(2 * secondsPerYear), "2 years")
XCTAssertEqual(format(2 * secondsPerYear + 1), "2 years, 1 second")
XCTAssertEqual(format(2 * secondsPerYear + 2), "2 years, 2 seconds")
XCTAssertEqual(format(2 * secondsPerYear + 1 * secondsPerWeek + 1), "2 years, 1 week, 1 second")
XCTAssertEqual(format(2 * secondsPerYear + 1 * secondsPerWeek + 2), "2 years, 1 week, 2 seconds")
XCTAssertEqual(format(2 * secondsPerYear + 2 * secondsPerWeek + 1), "2 years, 2 weeks, 1 second")
XCTAssertEqual(format(2 * secondsPerYear + 2 * secondsPerWeek + 2), "2 years, 2 weeks, 2 seconds")
let aVeryLongTime = 88 * secondsPerYear + 7 * secondsPerWeek + 6 * secondsPerDay + 5 * secondsPerHour + 4 * secondsPerMinute + 3
XCTAssertEqual(format(aVeryLongTime), "88 years, 7 weeks, 6 days, 5 hours, 4 minutes, 3 seconds")
}
func testIsStructurallyValidE164() {
let testCases: [(String, Bool)] = [
// E164 must have leading +.
("+5218341639157", true),
("5218341639157", false),
("+18018108311", true),
("18018108311", false),
// E164 must have exactly 1 leading +.
("++18018108311", false),
// E164 must only contains 0-9 arabic digits.
("+123a456", false),
("+123\u{0661}456", false), // ARABIC-INDIC DIGIT ONE
("+123\u{0031}\u{fe0f}\u{20e3}456", false), // KEYCAP DIGIT 1
// E164 must have at least 1 digit.
("+1", true),
("+", false),
// E164 must have no more than 19 digits.
("+1234567890123456789", true),
("+12345678901234567890", false),
// E164 must not start with a zero
("+0", false),
("+0123", false),
("+3210", true)
]
for (inputValue, expectedResult) in testCases {
XCTAssertEqual(inputValue.isStructurallyValidE164, expectedResult, "\(inputValue)")
}
}
func test_filterAsE164() {
XCTAssertEqual("", "".filteredAsE164)
XCTAssertEqual("", " ".filteredAsE164)
XCTAssertEqual("", "abc".filteredAsE164)
XCTAssertEqual("+123123", "abc+123+123zz".filteredAsE164)
XCTAssertEqual("+123123", ("+123" + "مرحبا بالعالم" + "abc+123zz").filteredAsE164)
XCTAssertEqual("+123123", "abc+123zz+123".filteredAsE164)
XCTAssertEqual("+123123", "abc+123zz+123🇨🇦".filteredAsE164)
XCTAssertEqual("", "🇨🇦".filteredAsE164)
XCTAssertEqual("1", "1🇨🇦".filteredAsE164)
XCTAssertEqual("1", "🇨🇦1".filteredAsE164)
XCTAssertEqual("", "🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦".filteredAsE164)
XCTAssertEqual("1", "1🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦".filteredAsE164)
XCTAssertEqual("1", "🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦🇨🇦1".filteredAsE164)
XCTAssertEqual("", "".filteredAsE164)
XCTAssertEqual("1", "1田".filteredAsE164)
XCTAssertEqual("1", "田1".filteredAsE164)
XCTAssertEqual("", "田田田田田田田".filteredAsE164)
XCTAssertEqual("1", "1田田田田田田".filteredAsE164)
XCTAssertEqual("1", "田田田田田田田1".filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "田中さんにあげて下さい" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "パーティーへ行かないか" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "和製漢語" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "部落格" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "사회과학원 어학연구소" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "찦차를 타고 온 펲시맨과 쑛다리 똠방각하" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "社會科學院語學研究所" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "울란바토르" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𠜎𠜱𠝹𠱓𠱸𠲖𠳏" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "表ポあA鷗Œé逍Üߪąñ丂㐀𠀀" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "ヽ༼ຈل͜ຈ༽ノ ヽ༼ຈل͜ຈ༽ノ" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "(。◕ ∀ ◕。)" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "`ィ(´∀`∩" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "__ロ(,_,*)" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "・( ̄∀ ̄)・:*:" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "゚・✿ヾ╲(。◕‿◕。)╱✿・゚" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + ",。・:*:・゜’( ☻ ω ☻ )。・:*:・゜’" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "(╯°□°)╯︵ ┻━┻)" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "(ノಥ益ಥ)ノ ┻━┻" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "┬─┬ノ( º _ ºノ)" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "( ͡° ͜ʖ ͡°)" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "¯\\_(ツ)_/¯" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "😍" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "👩🏽" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "👨‍🦰 👨🏿‍🦰 👨‍🦱 👨🏿‍🦱 🦹🏿‍♂️" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "👾 🙇 💁 🙅 🙆 🙋 🙎 🙍" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "🐵 🙈 🙉 🙊" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "✋🏿 💪🏿 👐🏿 🙌🏿 👏🏿 🙏🏿" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "👨‍👩‍👦 👨‍👩‍👧‍👦 👨‍👨‍👦 👩‍👩‍👧 👨‍👦 👨‍👧‍👦 👩‍👦 👩‍👧‍👦" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "🚾 🆒 🆓 🆕 🆖 🆗 🆙 🏧" + "321+").filteredAsE164)
XCTAssertEqual("+1230123456789321", ("+123" + "0⃣ 1⃣ 2⃣ 3⃣ 4⃣ 5⃣ 6⃣ 7⃣ 8⃣ 9⃣ 🔟" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "🇺🇸🇷🇺🇸🇦🇫🇦🇲" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "🇺🇸🇷🇺🇸🇦" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "١٢٣" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو." + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "בְּרֵאשִׁית, בָּרָא אֱלֹהִים, אֵת הַשָּׁמַיִם, וְאֵת הָאָרֶץ" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "הָיְתָהtestالصفحات التّحول" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،" + "321+").filteredAsE164)
XCTAssertEqual("+1235321", ("+123" + "الكل في المجمو عة (5)" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "᚛ᚄᚓᚐᚋᚒᚄ ᚑᚄᚂᚑᚏᚅ᚜" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "᚛                 ᚜" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "˙ɐnbᴉlɐ ɐuƃɐɯ ǝɹolop ʇǝ ǝɹoqɐl ʇn ʇunpᴉpᴉɔuᴉ ɹodɯǝʇ poɯsnᴉǝ op pǝs 'ʇᴉlǝ ƃuᴉɔsᴉdᴉpɐ ɹnʇǝʇɔǝsuoɔ 'ʇǝɯɐ ʇᴉs ɹolop ɯnsdᴉ ɯǝɹo˥" + "321+").filteredAsE164)
XCTAssertEqual("+12300321", ("+123" + "00˙Ɩ$-" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + " " + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗" + "321+").filteredAsE164)
XCTAssertEqual("+1230321", ("+123" + "🏳0🌈" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "జ్ఞ‌ా" + "321+").filteredAsE164)
XCTAssertEqual("+123321", ("+123" + "گچپژ" + "321+").filteredAsE164)
}
func test_ensureArabicNumerals() {
let zeroToNineTests: [String] = [
"০১২৩৪৫৬৭৮৯", // Bengali
"၀၁၂၃၄၅၆၇၈၉", // Burmese
"〇一二三四五六七八九", // Chinese (Simplified), Japanese
"零一二三四五六七八九", // Chinese (Traditional)
"०१२३४५६७८९", // Devanagari (Sanskrit, Hindi, and other Indian languages)
"٠١٢٣٤٥٦٧٨٩", // Eastern Arabic
"૦૧૨૩૪૫૬૭૮૯", // Gujarati
"੦੧੨੩੪੫੬੭੮੯", // Gurmukhi (Punjabi)
"೦೧೨೩೪೫೬೭೮೯", // Kannada
"൦൧൨൩൪൫൬൭൮൯", // Malayalam
"୦୧୨୩୪୫୬୭୮୯", // Odia
"۰۱۲۳۴۵۶۷۸۹", // Persian, Urdu
"௦௧௨௩௪௫௬௭௮௯", // Tamil
"౦౧౨౩౪౫౬౭౮౯", // Telugu
"๐๑๒๓๔๕๖๗๘๙", // Thai
"0123456789", // Western arabic
]
for zeroToNineTest in zeroToNineTests {
XCTAssertEqual(zeroToNineTest.ensureArabicNumerals, "0123456789")
}
// In mixed strings, only replaces the numerals.
XCTAssertEqual("نمرا ١٢٣٤٥ يا".ensureArabicNumerals, "نمرا 12345 يا")
// Appropriately handles characters that extend across multiple unicode scalars
XCTAssertEqual("123 👩🏻‍🔬🧛🏿‍♀️🤦🏽‍♀️🏳️‍🌈 ١٢٣".ensureArabicNumerals, "123 👩🏻‍🔬🧛🏿‍♀️🤦🏽‍♀️🏳️‍🌈 123")
// In strings without numerals, does nothing.
XCTAssertEqual("a".ensureArabicNumerals, "a")
XCTAssertEqual("".ensureArabicNumerals, "")
}
func testTrimToGlyphCount() {
XCTAssertEqual("🥹🥷🥹🥷🥹".trimToGlyphCount(4), "🥹🥷🥹🥷")
}
func testTrimmedIfNeeded() {
XCTAssertEqual("🥹🥷🥹🥷🥹".trimmedIfNeeded(maxGlyphCount: 4), "🥹🥷🥹🥷")
XCTAssertEqual("🥹🥷🥹🥷🥹".trimmedIfNeeded(maxGlyphCount: 5), nil)
XCTAssertEqual("🥹🥷🥹🥷🥹".trimmedIfNeeded(maxGlyphCount: 6), nil)
}
func testTrimToUtf8Count() {
XCTAssertEqual("🥹🥷🥹🥷🥹".trimToUtf8ByteCount(9), "🥹🥷")
}
}