mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-26 11:51:10 +01:00
Add support for OTA emoji download.
This commit is contained in:
committed by
Cody Henthorne
parent
7fa200401c
commit
85e0e74bc6
@@ -0,0 +1,138 @@
|
||||
package org.thoughtcrime.securesms.emoji
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import com.fasterxml.jackson.core.JsonParseException
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.thoughtcrime.securesms.components.emoji.CompositeEmojiPageModel
|
||||
import org.thoughtcrime.securesms.components.emoji.Emoji
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel
|
||||
import org.thoughtcrime.securesms.components.emoji.StaticEmojiPageModel
|
||||
|
||||
private const val INVALID_JSON = "{{}"
|
||||
private const val EMPTY_JSON = "{}"
|
||||
private const val SAMPLE_JSON_WITHOUT_OBSOLETE = """
|
||||
{
|
||||
"emoji": {
|
||||
"Places": [["d83cdf0d"], ["0003", "0004", "0005"]],
|
||||
"Foods": [["0001"], ["0002", "0003", "0004"]]
|
||||
},
|
||||
"metrics": {
|
||||
"raw_height": 64,
|
||||
"raw_width": 64,
|
||||
"per_row": 16
|
||||
},
|
||||
"densities": [ "xhdpi" ],
|
||||
"format": "png"
|
||||
}
|
||||
"""
|
||||
|
||||
private const val SAMPLE_JSON_WITH_OBSOLETE = """
|
||||
{
|
||||
"emoji": {
|
||||
"Places_1": [["0002"], ["0003", "0004", "0005"]],
|
||||
"Places_2": [["0003"], ["0008", "0009", "0000"]],
|
||||
"Foods": [["0001"], ["0002", "0003", "0004"]]
|
||||
},
|
||||
"obsolete": [
|
||||
{"obsoleted": "0012", "replace_with": "0023"}
|
||||
],
|
||||
"metrics": {
|
||||
"raw_height": 64,
|
||||
"raw_width": 64,
|
||||
"per_row": 16
|
||||
},
|
||||
"densities": [ "xhdpi" ],
|
||||
"format": "png"
|
||||
}
|
||||
"""
|
||||
|
||||
private val SAMPLE_JSON_WITHOUT_OBSOLETE_EXPECTED = listOf(
|
||||
StaticEmojiPageModel(EmojiCategory.FOODS.icon, listOf(Emoji("\u0001"), Emoji("\u0002", "\u0003", "\u0004")), Uri.parse("file:///Foods")),
|
||||
StaticEmojiPageModel(EmojiCategory.PLACES.icon, listOf(Emoji("\ud83c\udf0d"), Emoji("\u0003", "\u0004", "\u0005")), Uri.parse("file:///Places"))
|
||||
)
|
||||
|
||||
private val SAMPLE_JSON_WITH_OBSOLETE_EXPECTED_DISPLAY = listOf(
|
||||
StaticEmojiPageModel(EmojiCategory.FOODS.icon, listOf(Emoji("\u0001"), Emoji("\u0002", "\u0003", "\u0004")), Uri.parse("file:///Foods")),
|
||||
CompositeEmojiPageModel(
|
||||
EmojiCategory.PLACES.icon,
|
||||
listOf(
|
||||
StaticEmojiPageModel(EmojiCategory.PLACES.icon, listOf(Emoji("\u0002"), Emoji("\u0003", "\u0004", "\u0005")), Uri.parse("file:///Places_1")),
|
||||
StaticEmojiPageModel(EmojiCategory.PLACES.icon, listOf(Emoji("\u0003"), Emoji("\u0008", "\u0009", "\u0000")), Uri.parse("file:///Places_2"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private val SAMPLE_JSON_WITH_OBSOLETE_EXPECTED_DATA = listOf(
|
||||
StaticEmojiPageModel(EmojiCategory.FOODS.icon, listOf(Emoji("\u0001"), Emoji("\u0002", "\u0003", "\u0004")), Uri.parse("file:///Foods")),
|
||||
StaticEmojiPageModel(EmojiCategory.PLACES.icon, listOf(Emoji("\u0002"), Emoji("\u0003", "\u0004", "\u0005")), Uri.parse("file:///Places_1")),
|
||||
StaticEmojiPageModel(EmojiCategory.PLACES.icon, listOf(Emoji("\u0003"), Emoji("\u0008", "\u0009", "\u0000")), Uri.parse("file:///Places_2"))
|
||||
)
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
class EmojiJsonParserTest {
|
||||
|
||||
@Test(expected = NullPointerException::class)
|
||||
fun `Given empty json, when I parse, then I expect a NullPointerException`() {
|
||||
val result = EmojiJsonParser.parse(EMPTY_JSON.byteInputStream(), this::uriFactory)
|
||||
|
||||
result.getOrThrow()
|
||||
}
|
||||
|
||||
@Test(expected = JsonParseException::class)
|
||||
fun `Given invalid json, when I parse, then I expect a JsonParseException`() {
|
||||
val result = EmojiJsonParser.parse(INVALID_JSON.byteInputStream(), this::uriFactory)
|
||||
|
||||
result.getOrThrow()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given sample without obselete, when I parse, then I expect source without obsolete`() {
|
||||
val result: ParsedEmojiData = EmojiJsonParser.parse(SAMPLE_JSON_WITHOUT_OBSOLETE.byteInputStream(), this::uriFactory).getOrThrow()
|
||||
|
||||
Assert.assertTrue(result.obsolete.isEmpty())
|
||||
Assert.assertTrue(result.displayPages == result.dataPages)
|
||||
Assert.assertEquals(SAMPLE_JSON_WITHOUT_OBSOLETE_EXPECTED.size, result.dataPages.size)
|
||||
|
||||
result.dataPages.zip(SAMPLE_JSON_WITHOUT_OBSOLETE_EXPECTED).forEach { (actual, expected) ->
|
||||
Assert.assertTrue(actual.isSameAs(expected))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given sample with obsolete, when I parse, then I expect source with obsolete`() {
|
||||
val result: ParsedEmojiData = EmojiJsonParser.parse(SAMPLE_JSON_WITH_OBSOLETE.byteInputStream(), this::uriFactory).getOrThrow()
|
||||
|
||||
Assert.assertTrue(result.obsolete.size == 1)
|
||||
Assert.assertEquals("\u0012", result.obsolete[0].obsolete)
|
||||
Assert.assertEquals("\u0023", result.obsolete[0].replaceWith)
|
||||
Assert.assertFalse(result.displayPages == result.dataPages)
|
||||
Assert.assertEquals(SAMPLE_JSON_WITH_OBSOLETE_EXPECTED_DISPLAY.size, result.displayPages.size)
|
||||
|
||||
result.displayPages.zip(SAMPLE_JSON_WITH_OBSOLETE_EXPECTED_DISPLAY).forEach { (actual, expected) ->
|
||||
Assert.assertTrue(actual.isSameAs(expected))
|
||||
}
|
||||
|
||||
Assert.assertEquals(SAMPLE_JSON_WITH_OBSOLETE_EXPECTED_DATA.size, result.dataPages.size)
|
||||
|
||||
result.dataPages.zip(SAMPLE_JSON_WITH_OBSOLETE_EXPECTED_DATA).forEach { (actual, expected) ->
|
||||
Assert.assertTrue(actual.isSameAs(expected))
|
||||
}
|
||||
|
||||
Assert.assertEquals(result.densities, listOf("xhdpi"))
|
||||
Assert.assertEquals(result.format, "png")
|
||||
}
|
||||
|
||||
private fun uriFactory(sprite: String, format: String) = Uri.parse("file:///$sprite")
|
||||
|
||||
private fun EmojiPageModel.isSameAs(other: EmojiPageModel) =
|
||||
this.javaClass == other.javaClass &&
|
||||
this.emoji == other.emoji &&
|
||||
this.iconAttr == other.iconAttr &&
|
||||
this.spriteUri == other.spriteUri
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.thoughtcrime.securesms.emoji
|
||||
|
||||
import android.net.Uri
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.thoughtcrime.securesms.components.emoji.Emoji
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel
|
||||
|
||||
class EmojiSourceTest {
|
||||
|
||||
@Test
|
||||
fun `Given a bunch of data pages with max value 100100, when I get the maxEmojiLength, then I expect 6`() {
|
||||
val emojiDataFake = ParsedEmojiData(EmojiMetrics(-1, -1, -1), listOf(), "png", listOf(), dataPages = generatePages(), listOf())
|
||||
val testSubject = EmojiSource(0f, emojiDataFake, ::EmojiPageReference)
|
||||
|
||||
Assert.assertEquals(6, testSubject.maxEmojiLength)
|
||||
}
|
||||
|
||||
private fun generatePages() = (1..10).map { EmojiPageModelFake((1..100).shuffled().map { Emoji("$it$it") }) }
|
||||
|
||||
private class EmojiPageModelFake(private val displayE: List<Emoji>) : EmojiPageModel {
|
||||
|
||||
override fun getEmoji(): List<String> = displayE.map { it.variations }.flatten()
|
||||
|
||||
override fun getDisplayEmoji(): List<Emoji> = displayE
|
||||
|
||||
override fun getIconAttr(): Int = TODO("Not yet implemented")
|
||||
|
||||
override fun getSpriteUri(): Uri = TODO("Not yet implemented")
|
||||
|
||||
override fun isDynamic(): Boolean = TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user