282 lines
9.6 KiB
Swift
282 lines
9.6 KiB
Swift
//
|
||
// Copyright 2021 Signal Messenger, LLC
|
||
// SPDX-License-Identifier: AGPL-3.0-only
|
||
//
|
||
|
||
import XCTest
|
||
@testable import Signal
|
||
@testable import SignalServiceKit
|
||
|
||
class EmojiTests: XCTestCase {
|
||
func testSimpleEmojiCases() {
|
||
XCTAssertFalse("".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue("😃".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse("😃😃".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse("a".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse(" 😃".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse("😃 ".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue("👨👩👧👦".isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue("👨🏿🤝👨🏻".isSingleEmojiUsingEmojiWithSkinTones)
|
||
|
||
XCTAssertFalse("".isSingleEmoji)
|
||
XCTAssertTrue("😃".isSingleEmoji)
|
||
XCTAssertFalse("😃😃".isSingleEmoji)
|
||
XCTAssertFalse("a".isSingleEmoji)
|
||
XCTAssertFalse(" 😃".isSingleEmoji)
|
||
XCTAssertFalse("😃 ".isSingleEmoji)
|
||
XCTAssertTrue("👨👩👧👦".isSingleEmoji)
|
||
XCTAssertTrue("👨🏿🤝👨🏻".isSingleEmoji)
|
||
}
|
||
|
||
func testEmojiCounts() {
|
||
XCTAssertEqual("".glyphCount, 0)
|
||
XCTAssertEqual("😃".glyphCount, 1)
|
||
XCTAssertEqual("😃😃".glyphCount, 2)
|
||
XCTAssertEqual("a".glyphCount, 1)
|
||
XCTAssertEqual(" 😃".glyphCount, 2)
|
||
XCTAssertEqual("😃 ".glyphCount, 2)
|
||
XCTAssertEqual("👨👩👧👦".glyphCount, 1)
|
||
// CoreText considers this two glyphs,
|
||
// but glyphCount now uses String.count.
|
||
XCTAssertEqual("👨🏿🤝👨🏻".glyphCount, 1)
|
||
|
||
XCTAssertEqual("".count, 0)
|
||
XCTAssertEqual("😃".count, 1)
|
||
XCTAssertEqual("😃😃".count, 2)
|
||
XCTAssertEqual("a".count, 1)
|
||
XCTAssertEqual(" 😃".count, 2)
|
||
XCTAssertEqual("😃 ".count, 2)
|
||
XCTAssertEqual("👨👩👧👦".count, 1)
|
||
XCTAssertEqual("👨🏿🤝👨🏻".count, 1)
|
||
}
|
||
|
||
func testFancyEmojiCases() {
|
||
do {
|
||
// Valid emoji with skin tones.
|
||
let fancyEmoji = EmojiWithSkinTones(baseEmoji: .manWithGuaPiMao, skinTones: [.mediumDark]).rawValue
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Invalid emoji with skin tones.
|
||
let fancyEmoji = EmojiWithSkinTones(baseEmoji: .blueberries, skinTones: [.mediumDark]).rawValue
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Black Diamond Suit Emoji
|
||
let fancyEmoji = "\u{2666}" // ♦
|
||
// EmojiWithSkinTones doesn't recognize this as an emoji...
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
// But isSingleEmoji does...
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Black Diamond Suit Emoji
|
||
// Adding 'Variation Selector-16':
|
||
let fancyEmoji = "\u{2666}\u{FE0F}" // ♦️
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Thumbs up sign:
|
||
let fancyEmoji = "\u{1F44D}" // 👍
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Thumbs up sign:
|
||
// Adding 'Emoji Modifier Fitzpatrick Type-4':
|
||
let fancyEmoji = "\u{1F44D}\u{1F3FD}" // 👍🏽
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Man, Woman, Girl, Boy
|
||
let fancyEmoji = "\u{1F468}\u{1F469}\u{1F467}\u{1F466}" // 👨👩👧👦
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 4)
|
||
}
|
||
|
||
do {
|
||
// Man, Woman, Girl, Boy
|
||
// Adding 'Zero Width Joiner' between each
|
||
let fancyEmoji = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}" // 👨👩👧👦
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// This emoji has two skin tones.
|
||
// CoreText considers this two glyphs.
|
||
let fancyEmoji = "👨🏿🤝👨🏻"
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
let fancyEmoji = "🏳"
|
||
// EmojiWithSkinTones doesn't recognize this as an emoji...
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
// But isSingleEmoji does...
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
let fancyEmoji = "🌈️"
|
||
// EmojiWithSkinTones doesn't recognize this as an emoji...
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
// But isSingleEmoji does...
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Not an emoji.
|
||
let fancyEmoji = "a"
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Empty string.
|
||
let fancyEmoji = ""
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertFalse(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 0)
|
||
}
|
||
|
||
do {
|
||
// Not an emoji; just a isolated modifier.
|
||
// 'Emoji Modifier Fitzpatrick Type-4':
|
||
let fancyEmoji = "\u{1F3FD}"
|
||
// But this is considered an emoji by all measures.
|
||
XCTAssertTrue(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
|
||
do {
|
||
// Not an emoji; just a isolated modifier.
|
||
// 'Variation Selector-16':
|
||
let fancyEmoji = "\u{FE0F}"
|
||
// EmojiWithSkinTones doesn't recognize this as an emoji...
|
||
XCTAssertFalse(fancyEmoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
// But isSingleEmoji does...
|
||
XCTAssertTrue(fancyEmoji.isSingleEmoji)
|
||
XCTAssertEqual(fancyEmoji.count, 1)
|
||
}
|
||
}
|
||
|
||
func testMoreEmojiCases() {
|
||
let moreEmojis = [
|
||
"😍",
|
||
"👩🏽",
|
||
"👨🦰",
|
||
"👨🏿🦰",
|
||
"👨🦱",
|
||
"👨🏿🦱",
|
||
"🦹🏿♂️",
|
||
"👾",
|
||
"🙇",
|
||
"💁",
|
||
"🙅",
|
||
"🙆",
|
||
"🙋",
|
||
"🙎",
|
||
"🙍",
|
||
"🐵",
|
||
"🙈",
|
||
"🙉",
|
||
"🙊",
|
||
"❤️",
|
||
"💔",
|
||
"💌",
|
||
"💕",
|
||
"💞",
|
||
"💓",
|
||
"💗",
|
||
"💖",
|
||
"💘",
|
||
"💝",
|
||
"💟",
|
||
"💜",
|
||
"💛",
|
||
"💚",
|
||
"💙",
|
||
"✋🏿",
|
||
"💪🏿",
|
||
"👐🏿",
|
||
"🙌🏿",
|
||
"👏🏿",
|
||
"🙏🏿",
|
||
"👨👩👦",
|
||
"👨👩👧👦",
|
||
"👨👨👦",
|
||
"👩👩👧",
|
||
"👨👦",
|
||
"👨👧👦",
|
||
"👩👦",
|
||
"👩👧👦",
|
||
"🚾",
|
||
"🆒",
|
||
"🆓",
|
||
"🆕",
|
||
"🆖",
|
||
"🆗",
|
||
"🆙",
|
||
"🏧",
|
||
"0️⃣",
|
||
"1️⃣",
|
||
"2️⃣",
|
||
"3️⃣",
|
||
"4️⃣",
|
||
"5️⃣",
|
||
"6️⃣",
|
||
"7️⃣",
|
||
"8️⃣",
|
||
"9️⃣",
|
||
"🔟",
|
||
"🇦🇫",
|
||
"🇦🇲",
|
||
"🇺🇸",
|
||
"🇷🇺",
|
||
"🇸🇦",
|
||
"🇸🇦",
|
||
"🇨🇦"
|
||
]
|
||
for emoji in moreEmojis {
|
||
if !emoji.isSingleEmojiUsingEmojiWithSkinTones {
|
||
Logger.warn("!isSingleEmojiUsingEmojiWithSkinTones: '\(emoji)'")
|
||
}
|
||
XCTAssertTrue(emoji.isSingleEmojiUsingEmojiWithSkinTones)
|
||
|
||
if !emoji.isSingleEmoji {
|
||
Logger.warn("!isSingleEmoji: '\(emoji)'")
|
||
}
|
||
XCTAssertTrue(emoji.isSingleEmoji)
|
||
|
||
if emoji.count != 1 {
|
||
Logger.warn("'\(emoji)': \(emoji.count) != 1")
|
||
}
|
||
XCTAssertEqual(emoji.count, 1)
|
||
}
|
||
}
|
||
}
|