diff --git a/Scripts/emoji_ranges.py b/Scripts/emoji_ranges.py index 9a4263b67..d25e0e84c 100755 --- a/Scripts/emoji_ranges.py +++ b/Scripts/emoji_ranges.py @@ -70,7 +70,7 @@ if __name__ == '__main__': # if last_range_end + 1 == range_start: new_ranges = new_ranges[:-1] print 'merging', last_range_start, last_range_end, 'and', range_start, range_end - new_ranges.append((last_range_start, range_end,)) + new_ranges.append((last_range_start, max(range_end, last_range_end),)) continue new_ranges.append((range_start, range_end,)) diff --git a/Signal/src/util/DisplayableText.swift b/Signal/src/util/DisplayableText.swift index 9cfbd11c9..dd56ec71b 100644 --- a/Signal/src/util/DisplayableText.swift +++ b/Signal/src/util/DisplayableText.swift @@ -23,153 +23,64 @@ extension UnicodeScalar { // Current Version: // https://www.unicode.org/Public/emoji/6.0/emoji-data.txt static let kEmojiRanges = [ - EmojiRange(rangeStart:0x23, rangeEnd:0x23), - EmojiRange(rangeStart:0x2A, rangeEnd:0x2A), - EmojiRange(rangeStart:0x30, rangeEnd:0x39), - EmojiRange(rangeStart:0xA9, rangeEnd:0xA9), - EmojiRange(rangeStart:0xAE, rangeEnd:0xAE), - EmojiRange(rangeStart:0x200D, rangeEnd:0x200D), - EmojiRange(rangeStart:0x203C, rangeEnd:0x203C), - EmojiRange(rangeStart:0x2049, rangeEnd:0x2049), - EmojiRange(rangeStart:0x20D0, rangeEnd:0x20E3), - EmojiRange(rangeStart:0x2122, rangeEnd:0x2122), - EmojiRange(rangeStart:0x2139, rangeEnd:0x2139), - EmojiRange(rangeStart:0x2194, rangeEnd:0x2199), - EmojiRange(rangeStart:0x21A9, rangeEnd:0x21AA), - EmojiRange(rangeStart:0x231A, rangeEnd:0x231B), - EmojiRange(rangeStart:0x2328, rangeEnd:0x2328), - EmojiRange(rangeStart:0x2388, rangeEnd:0x2388), - EmojiRange(rangeStart:0x23CF, rangeEnd:0x23CF), - EmojiRange(rangeStart:0x23E9, rangeEnd:0x23F0), - EmojiRange(rangeStart:0x23F3, rangeEnd:0x23F3), - EmojiRange(rangeStart:0x23F8, rangeEnd:0x23FA), - EmojiRange(rangeStart:0x24C2, rangeEnd:0x24C2), - EmojiRange(rangeStart:0x25AA, rangeEnd:0x25AB), - EmojiRange(rangeStart:0x25B6, rangeEnd:0x25B6), - EmojiRange(rangeStart:0x25C0, rangeEnd:0x25C0), - EmojiRange(rangeStart:0x25FB, rangeEnd:0x25FE), - EmojiRange(rangeStart:0x2600, rangeEnd:0x260E), - EmojiRange(rangeStart:0x2611, rangeEnd:0x2611), - EmojiRange(rangeStart:0x2614, rangeEnd:0x261D), - EmojiRange(rangeStart:0x2620, rangeEnd:0x2620), - EmojiRange(rangeStart:0x2622, rangeEnd:0x2623), - EmojiRange(rangeStart:0x2626, rangeEnd:0x2626), - EmojiRange(rangeStart:0x262A, rangeEnd:0x262A), - EmojiRange(rangeStart:0x262E, rangeEnd:0x262F), - EmojiRange(rangeStart:0x2638, rangeEnd:0x263A), - EmojiRange(rangeStart:0x2640, rangeEnd:0x2640), - EmojiRange(rangeStart:0x2642, rangeEnd:0x2642), - EmojiRange(rangeStart:0x2648, rangeEnd:0x2653), - EmojiRange(rangeStart:0x2660, rangeEnd:0x2660), - EmojiRange(rangeStart:0x2663, rangeEnd:0x2663), - EmojiRange(rangeStart:0x2665, rangeEnd:0x2666), - EmojiRange(rangeStart:0x2668, rangeEnd:0x2668), - EmojiRange(rangeStart:0x2670, rangeEnd:0x267B), - EmojiRange(rangeStart:0x267E, rangeEnd:0x2693), - EmojiRange(rangeStart:0x2699, rangeEnd:0x2699), - EmojiRange(rangeStart:0x269B, rangeEnd:0x26AB), - EmojiRange(rangeStart:0x26B0, rangeEnd:0x26C8), - EmojiRange(rangeStart:0x26CE, rangeEnd:0x26D1), - EmojiRange(rangeStart:0x26D3, rangeEnd:0x26D4), - EmojiRange(rangeStart:0x26E2, rangeEnd:0x26EA), - EmojiRange(rangeStart:0x26F0, rangeEnd:0x26F3), - EmojiRange(rangeStart:0x26F5, rangeEnd:0x26F5), - EmojiRange(rangeStart:0x26F7, rangeEnd:0x26FA), - EmojiRange(rangeStart:0x26FD, rangeEnd:0x26FD), - EmojiRange(rangeStart:0x2700, rangeEnd:0x2702), - EmojiRange(rangeStart:0x2705, rangeEnd:0x2705), - EmojiRange(rangeStart:0x2708, rangeEnd:0x270F), - EmojiRange(rangeStart:0x2712, rangeEnd:0x2712), - EmojiRange(rangeStart:0x2714, rangeEnd:0x2714), - EmojiRange(rangeStart:0x2716, rangeEnd:0x2716), - EmojiRange(rangeStart:0x271D, rangeEnd:0x271D), - EmojiRange(rangeStart:0x2721, rangeEnd:0x2721), - EmojiRange(rangeStart:0x2728, rangeEnd:0x2728), - EmojiRange(rangeStart:0x2733, rangeEnd:0x2734), - EmojiRange(rangeStart:0x2744, rangeEnd:0x2744), - EmojiRange(rangeStart:0x2747, rangeEnd:0x2747), - EmojiRange(rangeStart:0x274C, rangeEnd:0x274C), - EmojiRange(rangeStart:0x274E, rangeEnd:0x274E), - EmojiRange(rangeStart:0x2753, rangeEnd:0x2755), - EmojiRange(rangeStart:0x2757, rangeEnd:0x2757), - EmojiRange(rangeStart:0x2763, rangeEnd:0x2767), - EmojiRange(rangeStart:0x2795, rangeEnd:0x2797), - EmojiRange(rangeStart:0x27A1, rangeEnd:0x27A1), - EmojiRange(rangeStart:0x27B0, rangeEnd:0x27B0), - EmojiRange(rangeStart:0x27BF, rangeEnd:0x27BF), - EmojiRange(rangeStart:0x2934, rangeEnd:0x2935), - EmojiRange(rangeStart:0x2B05, rangeEnd:0x2B07), - EmojiRange(rangeStart:0x2B1B, rangeEnd:0x2B1C), - EmojiRange(rangeStart:0x2B50, rangeEnd:0x2B50), - EmojiRange(rangeStart:0x2B55, rangeEnd:0x2B55), - EmojiRange(rangeStart:0x3030, rangeEnd:0x3030), - EmojiRange(rangeStart:0x303D, rangeEnd:0x303D), - EmojiRange(rangeStart:0x3297, rangeEnd:0x3297), - EmojiRange(rangeStart:0x3299, rangeEnd:0x3299), - EmojiRange(rangeStart:0xFE00, rangeEnd:0xFE0F), - EmojiRange(rangeStart:0x1F000, rangeEnd:0x1F004), - EmojiRange(rangeStart:0x1F02C, rangeEnd:0x1F0FF), - EmojiRange(rangeStart:0x1F10D, rangeEnd:0x1F10F), - EmojiRange(rangeStart:0x1F12F, rangeEnd:0x1F12F), - EmojiRange(rangeStart:0x1F16C, rangeEnd:0x1F171), - EmojiRange(rangeStart:0x1F17E, rangeEnd:0x1F17F), - EmojiRange(rangeStart:0x1F18E, rangeEnd:0x1F18E), - EmojiRange(rangeStart:0x1F191, rangeEnd:0x1F19A), - EmojiRange(rangeStart:0x1F1AD, rangeEnd:0x1F1FF), - EmojiRange(rangeStart:0x1F201, rangeEnd:0x1F20F), - EmojiRange(rangeStart:0x1F21A, rangeEnd:0x1F21A), - EmojiRange(rangeStart:0x1F22F, rangeEnd:0x1F22F), - EmojiRange(rangeStart:0x1F232, rangeEnd:0x1F23A), - EmojiRange(rangeStart:0x1F23C, rangeEnd:0x1F23F), - EmojiRange(rangeStart:0x1F249, rangeEnd:0x1F385), - EmojiRange(rangeStart:0x1F394, rangeEnd:0x1F397), - EmojiRange(rangeStart:0x1F399, rangeEnd:0x1F39B), - EmojiRange(rangeStart:0x1F39E, rangeEnd:0x1F3C7), - EmojiRange(rangeStart:0x1F3CA, rangeEnd:0x1F3F4), - EmojiRange(rangeStart:0x1F3F7, rangeEnd:0x1F450), - EmojiRange(rangeStart:0x1F466, rangeEnd:0x1F469), - EmojiRange(rangeStart:0x1F46E, rangeEnd:0x1F46E), - EmojiRange(rangeStart:0x1F470, rangeEnd:0x1F478), - EmojiRange(rangeStart:0x1F47C, rangeEnd:0x1F47C), - EmojiRange(rangeStart:0x1F481, rangeEnd:0x1F483), - EmojiRange(rangeStart:0x1F485, rangeEnd:0x1F487), - EmojiRange(rangeStart:0x1F4AA, rangeEnd:0x1F4AA), - EmojiRange(rangeStart:0x1F4F8, rangeEnd:0x1F570), - EmojiRange(rangeStart:0x1F573, rangeEnd:0x1F575), - EmojiRange(rangeStart:0x1F57A, rangeEnd:0x1F587), - EmojiRange(rangeStart:0x1F58A, rangeEnd:0x1F58D), - EmojiRange(rangeStart:0x1F590, rangeEnd:0x1F590), - EmojiRange(rangeStart:0x1F595, rangeEnd:0x1F596), - EmojiRange(rangeStart:0x1F5A4, rangeEnd:0x1F5A8), - EmojiRange(rangeStart:0x1F5B1, rangeEnd:0x1F5B2), - EmojiRange(rangeStart:0x1F5BC, rangeEnd:0x1F5BC), - EmojiRange(rangeStart:0x1F5C2, rangeEnd:0x1F5C4), - EmojiRange(rangeStart:0x1F5D1, rangeEnd:0x1F5D3), - EmojiRange(rangeStart:0x1F5DC, rangeEnd:0x1F5DE), - EmojiRange(rangeStart:0x1F5E1, rangeEnd:0x1F5E1), - EmojiRange(rangeStart:0x1F5E3, rangeEnd:0x1F5E3), - EmojiRange(rangeStart:0x1F5E8, rangeEnd:0x1F5E8), - EmojiRange(rangeStart:0x1F5EF, rangeEnd:0x1F5EF), - EmojiRange(rangeStart:0x1F5F3, rangeEnd:0x1F5F3), - EmojiRange(rangeStart:0x1F5FA, rangeEnd:0x1F64F), - EmojiRange(rangeStart:0x1F680, rangeEnd:0x1F6A3), - EmojiRange(rangeStart:0x1F6B4, rangeEnd:0x1F6B6), - EmojiRange(rangeStart:0x1F6C0, rangeEnd:0x1F6C0), - EmojiRange(rangeStart:0x1F6C6, rangeEnd:0x1F6CC), - EmojiRange(rangeStart:0x1F6D0, rangeEnd:0x1F6E9), - EmojiRange(rangeStart:0x1F6EB, rangeEnd:0x1F6FF), - EmojiRange(rangeStart:0x1F774, rangeEnd:0x1F77F), - EmojiRange(rangeStart:0x1F7D5, rangeEnd:0x1F7FF), - EmojiRange(rangeStart:0x1F80C, rangeEnd:0x1F80F), - EmojiRange(rangeStart:0x1F848, rangeEnd:0x1F84F), - EmojiRange(rangeStart:0x1F85A, rangeEnd:0x1F85F), - EmojiRange(rangeStart:0x1F888, rangeEnd:0x1F88F), - EmojiRange(rangeStart:0x1F8AE, rangeEnd:0x1F926), - EmojiRange(rangeStart:0x1F928, rangeEnd:0x1F93A), - EmojiRange(rangeStart:0x1F93C, rangeEnd:0x1F945), - EmojiRange(rangeStart:0x1F947, rangeEnd:0x1F9DD), - EmojiRange(rangeStart:0x1F9E7, rangeEnd:0x1FFFD), - EmojiRange(rangeStart:0xE0020, rangeEnd:0xE007F) + EmojiRange(rangeStart:0x23, rangeEnd:0x23), + EmojiRange(rangeStart:0x2A, rangeEnd:0x2A), + EmojiRange(rangeStart:0x30, rangeEnd:0x39), + EmojiRange(rangeStart:0xA9, rangeEnd:0xA9), + EmojiRange(rangeStart:0xAE, rangeEnd:0xAE), + EmojiRange(rangeStart:0x200D, rangeEnd:0x200D), + EmojiRange(rangeStart:0x203C, rangeEnd:0x203C), + EmojiRange(rangeStart:0x2049, rangeEnd:0x2049), + EmojiRange(rangeStart:0x20D0, rangeEnd:0x20FF), + EmojiRange(rangeStart:0x2122, rangeEnd:0x2122), + EmojiRange(rangeStart:0x2139, rangeEnd:0x2139), + EmojiRange(rangeStart:0x2194, rangeEnd:0x2199), + EmojiRange(rangeStart:0x21A9, rangeEnd:0x21AA), + EmojiRange(rangeStart:0x231A, rangeEnd:0x231B), + EmojiRange(rangeStart:0x2328, rangeEnd:0x2328), + EmojiRange(rangeStart:0x2388, rangeEnd:0x2388), + EmojiRange(rangeStart:0x23CF, rangeEnd:0x23CF), + EmojiRange(rangeStart:0x23E9, rangeEnd:0x23F3), + EmojiRange(rangeStart:0x23F8, rangeEnd:0x23FA), + EmojiRange(rangeStart:0x24C2, rangeEnd:0x24C2), + EmojiRange(rangeStart:0x25AA, rangeEnd:0x25AB), + EmojiRange(rangeStart:0x25B6, rangeEnd:0x25B6), + EmojiRange(rangeStart:0x25C0, rangeEnd:0x25C0), + EmojiRange(rangeStart:0x25FB, rangeEnd:0x25FE), + EmojiRange(rangeStart:0x2600, rangeEnd:0x27BF), + EmojiRange(rangeStart:0x2934, rangeEnd:0x2935), + EmojiRange(rangeStart:0x2B05, rangeEnd:0x2B07), + EmojiRange(rangeStart:0x2B1B, rangeEnd:0x2B1C), + EmojiRange(rangeStart:0x2B50, rangeEnd:0x2B50), + EmojiRange(rangeStart:0x2B55, rangeEnd:0x2B55), + EmojiRange(rangeStart:0x3030, rangeEnd:0x3030), + EmojiRange(rangeStart:0x303D, rangeEnd:0x303D), + EmojiRange(rangeStart:0x3297, rangeEnd:0x3297), + EmojiRange(rangeStart:0x3299, rangeEnd:0x3299), + EmojiRange(rangeStart:0xFE00, rangeEnd:0xFE0F), + EmojiRange(rangeStart:0x1F000, rangeEnd:0x1F0FF), + EmojiRange(rangeStart:0x1F10D, rangeEnd:0x1F10F), + EmojiRange(rangeStart:0x1F12F, rangeEnd:0x1F12F), + EmojiRange(rangeStart:0x1F16C, rangeEnd:0x1F171), + EmojiRange(rangeStart:0x1F17E, rangeEnd:0x1F17F), + EmojiRange(rangeStart:0x1F18E, rangeEnd:0x1F18E), + EmojiRange(rangeStart:0x1F191, rangeEnd:0x1F19A), + EmojiRange(rangeStart:0x1F1AD, rangeEnd:0x1F1FF), + EmojiRange(rangeStart:0x1F201, rangeEnd:0x1F20F), + EmojiRange(rangeStart:0x1F21A, rangeEnd:0x1F21A), + EmojiRange(rangeStart:0x1F22F, rangeEnd:0x1F22F), + EmojiRange(rangeStart:0x1F232, rangeEnd:0x1F23A), + EmojiRange(rangeStart:0x1F23C, rangeEnd:0x1F23F), + EmojiRange(rangeStart:0x1F249, rangeEnd:0x1F64F), + EmojiRange(rangeStart:0x1F680, rangeEnd:0x1F6FF), + EmojiRange(rangeStart:0x1F774, rangeEnd:0x1F77F), + EmojiRange(rangeStart:0x1F7D5, rangeEnd:0x1F7FF), + EmojiRange(rangeStart:0x1F80C, rangeEnd:0x1F80F), + EmojiRange(rangeStart:0x1F848, rangeEnd:0x1F84F), + EmojiRange(rangeStart:0x1F85A, rangeEnd:0x1F85F), + EmojiRange(rangeStart:0x1F888, rangeEnd:0x1F88F), + EmojiRange(rangeStart:0x1F8AE, rangeEnd:0x1FFFD), + EmojiRange(rangeStart:0xE0020, rangeEnd:0xE007F) ] var isEmoji: Bool { @@ -211,17 +122,14 @@ extension String { } var isSingleEmoji: Bool { - return glyphCount == 1 && containsEmoji } var containsEmoji: Bool { - return unicodeScalars.contains { $0.isEmoji } } var containsOnlyEmoji: Bool { - return !isEmpty && !unicodeScalars.contains(where: { !$0.isEmoji diff --git a/Signal/test/util/DisplayableTextFilterTest.swift b/Signal/test/util/DisplayableTextFilterTest.swift index 39dd329ac..9fbcf481b 100644 --- a/Signal/test/util/DisplayableTextFilterTest.swift +++ b/Signal/test/util/DisplayableTextFilterTest.swift @@ -17,24 +17,84 @@ class DisplayableTextTest: XCTestCase { } func testDisplayableText() { - // Ignore default byte size limitations to test other filtering behaviors - let filter = DisplayableText() - // show plain text let boringText = "boring text" - XCTAssertEqual(boringText, filter.displayableText(boringText)) + XCTAssertEqual(boringText, DisplayableText.displayableText(boringText)) // show high byte emojis let emojiText = "🇹🇹🌼🇹🇹🌼🇹🇹" - XCTAssertEqual(emojiText, filter.displayableText(emojiText)) + XCTAssertEqual(emojiText, DisplayableText.displayableText(emojiText)) // show normal diacritic usage let diacriticalText = "Příliš žluťoučký kůň úpěl ďábelské ódy." - XCTAssertEqual(diacriticalText, filter.displayableText(diacriticalText)) + XCTAssertEqual(diacriticalText, DisplayableText.displayableText(diacriticalText)) // filter excessive diacritics - XCTAssertEqual("HAVING TROUBLE READING TEXT?", filter.displayableText("H҉̸̧͘͠A͢͞V̛̛I̴̸N͏̕͏G҉̵͜͏͢ ̧̧́T̶̛͘͡R̸̵̨̢̀O̷̡U͡҉B̶̛͢͞L̸̸͘͢͟É̸ ̸̛͘͏R͟È͠͞A̸͝Ḑ̕͘͜I̵͘҉͜͞N̷̡̢͠G̴͘͠ ͟͞T͏̢́͡È̀X̕҉̢̀T̢͠?̕͏̢͘͢") ) + XCTAssertEqual("HAVING TROUBLE READING TEXT?", DisplayableText.displayableText("H҉̸̧͘͠A͢͞V̛̛I̴̸N͏̕͏G҉̵͜͏͢ ̧̧́T̶̛͘͡R̸̵̨̢̀O̷̡U͡҉B̶̛͢͞L̸̸͘͢͟É̸ ̸̛͘͏R͟È͠͞A̸͝Ḑ̕͘͜I̵͘҉͜͞N̷̡̢͠G̴͘͠ ͟͞T͏̢́͡È̀X̕҉̢̀T̢͠?̕͏̢͘͢") ) - XCTAssertEqual("LGO!", filter.displayableText("L̷̳͔̲͝Ģ̵̮̯̤̩̙͍̬̟͉̹̘̹͍͈̮̦̰̣͟͝O̶̴̮̻̮̗͘͡!̴̷̟͓͓")) + XCTAssertEqual("LGO!", DisplayableText.displayableText("L̷̳͔̲͝Ģ̵̮̯̤̩̙͍̬̟͉̹̘̹͍͈̮̦̰̣͟͝O̶̴̮̻̮̗͘͡!̴̷̟͓͓")) + } + + func testGlyphCount() { + // Plain text + XCTAssertEqual("boring text".glyphCount, 11) + + // Emojis + XCTAssertEqual("🇹🇹🌼🇹🇹🌼🇹🇹".glyphCount, 5) + XCTAssertEqual("🇹🇹".glyphCount, 1) + XCTAssertEqual("🇹🇹 ".glyphCount, 2) + XCTAssertEqual("👌🏽👌🏾👌🏿".glyphCount, 3) + XCTAssertEqual("😍".glyphCount, 1) + XCTAssertEqual("👩🏽".glyphCount, 1) + XCTAssertEqual("👾🙇💁🙅🙆🙋🙎🙍".glyphCount, 8) + XCTAssertEqual("🐵🙈🙉🙊".glyphCount, 4) + XCTAssertEqual("❤️💔💌💕💞💓💗💖💘💝💟💜💛💚💙".glyphCount, 15) + XCTAssertEqual("✋🏿💪🏿👐🏿🙌🏿👏🏿🙏🏿".glyphCount, 6) + XCTAssertEqual("🚾🆒🆓🆕🆖🆗🆙🏧".glyphCount, 8) + XCTAssertEqual("0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟".glyphCount, 11) + XCTAssertEqual("🇺🇸🇷🇺🇦🇫🇦🇲".glyphCount, 4) + XCTAssertEqual("🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸".glyphCount, 7) + XCTAssertEqual("🇺🇸🇷🇺🇸🇦🇫🇦🇲".glyphCount, 5) + XCTAssertEqual("🇺🇸🇷🇺🇸🇦".glyphCount, 3) + XCTAssertEqual("123".glyphCount, 3) + + // Normal diacritic usage + XCTAssertEqual("Příliš žluťoučký kůň úpěl ďábelské ódy.".glyphCount, 39) + + // Excessive diacritics + XCTAssertEqual("H҉̸̧͘͠A͢͞V̛̛I̴̸N͏̕͏G҉̵͜͏͢ ̧̧́T̶̛͘͡R̸̵̨̢̀O̷̡U͡҉B̶̛͢͞L̸̸͘͢͟É̸ ̸̛͘͏R͟È͠͞A̸͝Ḑ̕͘͜I̵͘҉͜͞N̷̡̢͠G̴͘͠ ͟͞T͏̢́͡È̀X̕҉̢̀T̢͠?̕͏̢͘͢".glyphCount, 115) + XCTAssertEqual("L̷̳͔̲͝Ģ̵̮̯̤̩̙͍̬̟͉̹̘̹͍͈̮̦̰̣͟͝O̶̴̮̻̮̗͘͡!̴̷̟͓͓".glyphCount, 43) + } + + func testContainsOnlyEmoji() { + // Plain text + XCTAssertFalse("boring text".containsOnlyEmoji) + + // Emojis + XCTAssertTrue("🇹🇹🌼🇹🇹🌼🇹🇹".containsOnlyEmoji) + XCTAssertTrue("🇹🇹".containsOnlyEmoji) + XCTAssertFalse("🇹🇹 ".containsOnlyEmoji) + XCTAssertTrue("👌🏽👌🏾👌🏿".containsOnlyEmoji) + XCTAssertTrue("😍".containsOnlyEmoji) + XCTAssertTrue("👩🏽".containsOnlyEmoji) + XCTAssertTrue("👾🙇💁🙅🙆🙋🙎🙍".containsOnlyEmoji) + XCTAssertTrue("🐵🙈🙉🙊".containsOnlyEmoji) + XCTAssertTrue("❤️💔💌💕💞💓💗💖💘💝💟💜💛💚💙".containsOnlyEmoji) + XCTAssertTrue("✋🏿💪🏿👐🏿🙌🏿👏🏿🙏🏿".containsOnlyEmoji) + XCTAssertTrue("🚾🆒🆓🆕🆖🆗🆙🏧".containsOnlyEmoji) + XCTAssertTrue("0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟".containsOnlyEmoji) + XCTAssertTrue("🇺🇸🇷🇺🇦🇫🇦🇲".containsOnlyEmoji) + XCTAssertFalse("🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸".containsOnlyEmoji) + XCTAssertTrue("🇺🇸🇷🇺🇸🇦🇫🇦🇲".containsOnlyEmoji) + XCTAssertTrue("🇺🇸🇷🇺🇸🇦".containsOnlyEmoji) + // Unicode standard doesn't consider these to be Emoji. + XCTAssertFalse("123".containsOnlyEmoji) + + // Normal diacritic usage + XCTAssertFalse("Příliš žluťoučký kůň úpěl ďábelské ódy.".containsOnlyEmoji) + + // Excessive diacritics + XCTAssertFalse("H҉̸̧͘͠A͢͞V̛̛I̴̸N͏̕͏G҉̵͜͏͢ ̧̧́T̶̛͘͡R̸̵̨̢̀O̷̡U͡҉B̶̛͢͞L̸̸͘͢͟É̸ ̸̛͘͏R͟È͠͞A̸͝Ḑ̕͘͜I̵͘҉͜͞N̷̡̢͠G̴͘͠ ͟͞T͏̢́͡È̀X̕҉̢̀T̢͠?̕͏̢͘͢".containsOnlyEmoji) + XCTAssertFalse("L̷̳͔̲͝Ģ̵̮̯̤̩̙͍̬̟͉̹̘̹͍͈̮̦̰̣͟͝O̶̴̮̻̮̗͘͡!̴̷̟͓͓".containsOnlyEmoji) } }