1 /* <lambda>null2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.compose.ui.text.platform 18 19 import android.graphics.Typeface 20 import androidx.compose.runtime.State 21 import androidx.emoji2.text.EmojiCompat 22 import androidx.emoji2.text.EmojiCompat.LOAD_STRATEGY_MANUAL 23 import androidx.emoji2.text.EmojiCompat.MetadataRepoLoader 24 import androidx.emoji2.text.MetadataRepo 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.SmallTest 27 import com.google.common.truth.Truth.assertThat 28 import kotlinx.coroutines.CompletableDeferred 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.Dispatchers 31 import kotlinx.coroutines.delay 32 import kotlinx.coroutines.launch 33 import kotlinx.coroutines.runBlocking 34 import kotlinx.coroutines.withTimeout 35 import org.junit.After 36 import org.junit.Before 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 40 @RunWith(AndroidJUnit4::class) 41 @SmallTest 42 class EmojiCompatStatusTest { 43 44 @Before 45 fun reset() { 46 EmojiCompat.reset(null) 47 EmojiCompatStatus.setDelegateForTesting(null) 48 } 49 50 @After 51 fun clean() { 52 EmojiCompat.reset(null) 53 EmojiCompatStatus.setDelegateForTesting(null) 54 } 55 56 @Test 57 fun nonConfiguredEc_isNotLoaded() { 58 EmojiCompat.reset(null) 59 assertThat(EmojiCompatStatus.fontLoaded.value).isFalse() 60 } 61 62 @Test 63 fun default_isNotLoaded() { 64 val (config, deferred) = makeEmojiConfig() 65 EmojiCompat.init(config) 66 assertThat(EmojiCompatStatus.fontLoaded.value).isFalse() 67 deferred.complete(null) 68 } 69 70 @Test 71 fun loading_isNotLoaded() { 72 val (config, deferred) = makeEmojiConfig() 73 val ec = EmojiCompat.init(config) 74 ec.load() 75 assertThat(EmojiCompatStatus.fontLoaded.value).isFalse() 76 deferred.complete(null) 77 } 78 79 @Test 80 fun error_isNotLoaded() { 81 val (config, deferred) = makeEmojiConfig() 82 val ec = EmojiCompat.init(config) 83 ec.load() 84 deferred.complete(null) 85 assertThat(EmojiCompatStatus.fontLoaded.value).isFalse() 86 } 87 88 @Test 89 fun loaded_isLoaded() { 90 val (config, deferred) = makeEmojiConfig() 91 val ec = EmojiCompat.init(config) 92 deferred.complete(MetadataRepo.create(Typeface.DEFAULT)) 93 ec.load() 94 // query now, after init EC 95 EmojiCompatStatus.setDelegateForTesting(null) 96 EmojiCompatStatus.fontLoaded.assertTrue() 97 } 98 99 @Test 100 fun nonLoaded_toLoaded_updatesReturnState() { 101 val (config, deferred) = makeEmojiConfig() 102 val ec = EmojiCompat.init(config) 103 val state = EmojiCompatStatus.fontLoaded 104 assertThat(state.value).isFalse() 105 106 deferred.complete(MetadataRepo.create(Typeface.DEFAULT)) 107 ec.load() 108 109 state.assertTrue() 110 } 111 112 @Test 113 fun nonConfigured_canLoadLater() { 114 EmojiCompat.reset(null) 115 val initialFontLoad = EmojiCompatStatus.fontLoaded 116 assertThat(initialFontLoad.value).isFalse() 117 118 val (config, deferred) = makeEmojiConfig() 119 val ec = EmojiCompat.init(config) 120 deferred.complete(MetadataRepo.create(Typeface.DEFAULT)) 121 ec.load() 122 123 EmojiCompatStatus.fontLoaded.assertTrue() 124 } 125 126 private fun State<Boolean>.assertTrue() { 127 // there's too many async actors to do anything reasonable and non-flaky here. tie up the 128 // test thread until main posts the value 129 runBlocking { 130 withTimeout(1000) { while (!value) delay(0) } 131 assertThat(value).isTrue() 132 } 133 } 134 135 private fun makeEmojiConfig(): Pair<EmojiCompat.Config, CompletableDeferred<MetadataRepo?>> { 136 val deferred = CompletableDeferred<MetadataRepo?>(null) 137 val loader = MetadataRepoLoader { cb -> 138 CoroutineScope(Dispatchers.Default).launch { 139 val result = deferred.await() 140 if (result != null) { 141 cb.onLoaded(result) 142 } else { 143 cb.onFailed(IllegalStateException("No")) 144 } 145 } 146 } 147 val config = object : EmojiCompat.Config(loader) {} 148 config.setMetadataLoadStrategy(LOAD_STRATEGY_MANUAL) 149 return config to deferred 150 } 151 } 152