Browse Source

Add TestComposeEnvironment for resource tests (#4056)

pull/4057/head
Konstantin 11 months ago committed by GitHub
parent
commit
4c6bebb237
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 44
      components/resources/library/src/androidInstrumentedTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.android.kt
  2. 2
      components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/FontResources.android.kt
  3. 2
      components/resources/library/src/blockingMain/kotlin/org/jetbrains/compose/resources/ResourceState.blocking.kt
  4. 2
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ImageResources.kt
  5. 36
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceEnvironment.kt
  6. 13
      components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/TestComposeEnvironment.kt
  7. 40
      components/resources/library/src/desktopTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.desktop.kt
  8. 2
      components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt

44
components/resources/library/src/androidInstrumentedTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.android.kt

@ -2,9 +2,7 @@ package org.jetbrains.compose.resources
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.*
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.runComposeUiTest import androidx.compose.ui.test.runComposeUiTest
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -29,10 +27,12 @@ class ComposeResourceTest {
val imagePathFlow = MutableStateFlow(DrawableResource("1.png")) val imagePathFlow = MutableStateFlow(DrawableResource("1.png"))
val recompositionsCounter = RecompositionsCounter() val recompositionsCounter = RecompositionsCounter()
setContent { setContent {
val res by imagePathFlow.collectAsState() CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
val imgRes = imageResource(res) val res by imagePathFlow.collectAsState()
recompositionsCounter.content { val imgRes = imageResource(res)
Image(bitmap = imgRes, contentDescription = null) recompositionsCounter.content {
Image(bitmap = imgRes, contentDescription = null)
}
} }
} }
awaitIdle() awaitIdle()
@ -48,7 +48,10 @@ class ComposeResourceTest {
val testResourceReader = TestResourceReader() val testResourceReader = TestResourceReader()
val imagePathFlow = MutableStateFlow(DrawableResource("1.png")) val imagePathFlow = MutableStateFlow(DrawableResource("1.png"))
setContent { setContent {
CompositionLocalProvider(LocalResourceReader provides testResourceReader) { CompositionLocalProvider(
LocalResourceReader provides testResourceReader,
LocalComposeEnvironment provides TestComposeEnvironment
) {
val res by imagePathFlow.collectAsState() val res by imagePathFlow.collectAsState()
Image(painterResource(res), null) Image(painterResource(res), null)
} }
@ -72,7 +75,10 @@ class ComposeResourceTest {
val testResourceReader = TestResourceReader() val testResourceReader = TestResourceReader()
val stringIdFlow = MutableStateFlow(TestStringResource("app_name")) val stringIdFlow = MutableStateFlow(TestStringResource("app_name"))
setContent { setContent {
CompositionLocalProvider(LocalResourceReader provides testResourceReader) { CompositionLocalProvider(
LocalResourceReader provides testResourceReader,
LocalComposeEnvironment provides TestComposeEnvironment
) {
val res by stringIdFlow.collectAsState() val res by stringIdFlow.collectAsState()
Text(stringResource(res)) Text(stringResource(res))
Text(stringArrayResource(TestStringResource("str_arr")).joinToString()) Text(stringArrayResource(TestStringResource("str_arr")).joinToString())
@ -95,12 +101,20 @@ class ComposeResourceTest {
fun testReadStringResource() = runComposeUiTest { fun testReadStringResource() = runComposeUiTest {
runBlockingTest { runBlockingTest {
setContent { setContent {
assertEquals("Compose Resources App", stringResource(TestStringResource("app_name"))) CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
assertEquals( assertEquals(
"Hello, test-name! You have 42 new messages.", "Compose Resources App",
stringResource(TestStringResource("str_template"), "test-name", 42) stringResource(TestStringResource("app_name"))
) )
assertEquals(listOf("item 1", "item 2", "item 3"), stringArrayResource(TestStringResource("str_arr"))) assertEquals(
"Hello, test-name! You have 42 new messages.",
stringResource(TestStringResource("str_template"), "test-name", 42)
)
assertEquals(
listOf("item 1", "item 2", "item 3"),
stringArrayResource(TestStringResource("str_arr"))
)
}
} }
awaitIdle() awaitIdle()
} }

2
components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/FontResources.android.kt

@ -8,7 +8,7 @@ import androidx.compose.ui.text.font.*
@ExperimentalResourceApi @ExperimentalResourceApi
@Composable @Composable
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font { actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font {
val environment = rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val path = remember(environment) { resource.getPathByEnvironment(environment) } val path = remember(environment) { resource.getPathByEnvironment(environment) }
return Font(path, LocalContext.current.assets, weight, style) return Font(path, LocalContext.current.assets, weight, style)
} }

2
components/resources/library/src/blockingMain/kotlin/org/jetbrains/compose/resources/ResourceState.blocking.kt

@ -9,7 +9,7 @@ internal actual fun <T> rememberResourceState(
getDefault: () -> T, getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key, environment) { return remember(key, environment) {
mutableStateOf( mutableStateOf(
runBlocking { block(environment) } runBlocking { block(environment) }

2
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ImageResources.kt

@ -44,7 +44,7 @@ fun DrawableResource(path: String): DrawableResource = DrawableResource(
@ExperimentalResourceApi @ExperimentalResourceApi
@Composable @Composable
fun painterResource(resource: DrawableResource): Painter { fun painterResource(resource: DrawableResource): Painter {
val environment = rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val filePath = remember(resource, environment) { resource.getPathByEnvironment(environment) } val filePath = remember(resource, environment) { resource.getPathByEnvironment(environment) }
val isXml = filePath.endsWith(".xml", true) val isXml = filePath.endsWith(".xml", true)
if (isXml) { if (isXml) {

36
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceEnvironment.kt

@ -12,23 +12,33 @@ internal data class ResourceEnvironment(
val density: DensityQualifier val density: DensityQualifier
) )
@Composable internal interface ComposeEnvironment {
internal fun rememberEnvironment(): ResourceEnvironment { @Composable
val composeLocale = Locale.current fun rememberEnvironment(): ResourceEnvironment
val composeTheme = isSystemInDarkTheme() }
val composeDensity = LocalDensity.current
internal val DefaultComposeEnvironment = object : ComposeEnvironment {
@Composable
override fun rememberEnvironment(): ResourceEnvironment {
val composeLocale = Locale.current
val composeTheme = isSystemInDarkTheme()
val composeDensity = LocalDensity.current
//cache ResourceEnvironment unless compose environment is changed //cache ResourceEnvironment unless compose environment is changed
return remember(composeLocale, composeTheme, composeDensity) { return remember(composeLocale, composeTheme, composeDensity) {
ResourceEnvironment( ResourceEnvironment(
LanguageQualifier(composeLocale.language), LanguageQualifier(composeLocale.language),
RegionQualifier(composeLocale.region), RegionQualifier(composeLocale.region),
ThemeQualifier.selectByValue(composeTheme), ThemeQualifier.selectByValue(composeTheme),
DensityQualifier.selectByDensity(composeDensity.density) DensityQualifier.selectByDensity(composeDensity.density)
) )
}
} }
} }
//ComposeEnvironment provider will be overridden for tests
internal val LocalComposeEnvironment = staticCompositionLocalOf { DefaultComposeEnvironment }
/** /**
* Provides the resource environment for non-composable access to string resources. * Provides the resource environment for non-composable access to string resources.
* It is an expensive operation! Don't use it in composable functions with no cache! * It is an expensive operation! Don't use it in composable functions with no cache!

13
components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/TestComposeEnvironment.kt

@ -0,0 +1,13 @@
package org.jetbrains.compose.resources
import androidx.compose.runtime.Composable
internal val TestComposeEnvironment = object : ComposeEnvironment {
@Composable
override fun rememberEnvironment() = ResourceEnvironment(
language = LanguageQualifier("en"),
region = RegionQualifier("US"),
theme = ThemeQualifier.LIGHT,
density = DensityQualifier.XHDPI
)
}

40
components/resources/library/src/desktopTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.desktop.kt

@ -27,10 +27,12 @@ class ComposeResourceTest {
val imagePathFlow = MutableStateFlow(DrawableResource("1.png")) val imagePathFlow = MutableStateFlow(DrawableResource("1.png"))
val recompositionsCounter = RecompositionsCounter() val recompositionsCounter = RecompositionsCounter()
setContent { setContent {
val res by imagePathFlow.collectAsState() CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
val imgRes = imageResource(res) val res by imagePathFlow.collectAsState()
recompositionsCounter.content { val imgRes = imageResource(res)
Image(bitmap = imgRes, contentDescription = null) recompositionsCounter.content {
Image(bitmap = imgRes, contentDescription = null)
}
} }
} }
awaitIdle() awaitIdle()
@ -46,7 +48,10 @@ class ComposeResourceTest {
val testResourceReader = TestResourceReader() val testResourceReader = TestResourceReader()
val imagePathFlow = MutableStateFlow(DrawableResource("1.png")) val imagePathFlow = MutableStateFlow(DrawableResource("1.png"))
setContent { setContent {
CompositionLocalProvider(LocalResourceReader provides testResourceReader) { CompositionLocalProvider(
LocalResourceReader provides testResourceReader,
LocalComposeEnvironment provides TestComposeEnvironment
) {
val res by imagePathFlow.collectAsState() val res by imagePathFlow.collectAsState()
Image(painterResource(res), null) Image(painterResource(res), null)
} }
@ -70,7 +75,10 @@ class ComposeResourceTest {
val testResourceReader = TestResourceReader() val testResourceReader = TestResourceReader()
val stringIdFlow = MutableStateFlow(TestStringResource("app_name")) val stringIdFlow = MutableStateFlow(TestStringResource("app_name"))
setContent { setContent {
CompositionLocalProvider(LocalResourceReader provides testResourceReader) { CompositionLocalProvider(
LocalResourceReader provides testResourceReader,
LocalComposeEnvironment provides TestComposeEnvironment
) {
val res by stringIdFlow.collectAsState() val res by stringIdFlow.collectAsState()
Text(stringResource(res)) Text(stringResource(res))
Text(stringArrayResource(TestStringResource("str_arr")).joinToString()) Text(stringArrayResource(TestStringResource("str_arr")).joinToString())
@ -93,12 +101,20 @@ class ComposeResourceTest {
fun testReadStringResource() = runComposeUiTest { fun testReadStringResource() = runComposeUiTest {
runBlockingTest { runBlockingTest {
setContent { setContent {
assertEquals("Compose Resources App", stringResource(TestStringResource("app_name"))) CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
assertEquals( assertEquals(
"Hello, test-name! You have 42 new messages.", "Compose Resources App",
stringResource(TestStringResource("str_template"), "test-name", 42) stringResource(TestStringResource("app_name"))
) )
assertEquals(listOf("item 1", "item 2", "item 3"), stringArrayResource(TestStringResource("str_arr"))) assertEquals(
"Hello, test-name! You have 42 new messages.",
stringResource(TestStringResource("str_template"), "test-name", 42)
)
assertEquals(
listOf("item 1", "item 2", "item 3"),
stringArrayResource(TestStringResource("str_arr"))
)
}
} }
awaitIdle() awaitIdle()
} }

2
components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt

@ -12,7 +12,7 @@ internal actual fun <T> rememberResourceState(
getDefault: () -> T, getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key) { mutableStateOf(getDefault()) } val state = remember(key) { mutableStateOf(getDefault()) }
LaunchedEffect(key) { LaunchedEffect(key) {
state.value = block(environment) state.value = block(environment)

Loading…
Cancel
Save