Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change isIdle in tests to return true when there exist delayed tasks #1550

Merged
merged 5 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal class TooltipAreaTest {
onNodeWithTag("elementWithTooltip").performMouseInput {
moveTo(Offset(30f, 40f))
}

mainClock.advanceTimeBy(TooltipDelayMillis + 1L)
onNodeWithTag("tooltip").assertExists()
}

Expand All @@ -64,6 +64,7 @@ internal class TooltipAreaTest {
onNodeWithTag("elementWithTooltip").performMouseInput {
moveTo(Offset(30f, 40f))
}
mainClock.advanceTimeBy(TooltipDelayMillis + 1L)
onNodeWithTag("tooltip").assertExists()

onNodeWithTag("elementWithTooltip").performMouseInput {
Expand All @@ -84,6 +85,7 @@ internal class TooltipAreaTest {
onNodeWithTag("elementWithTooltip").performMouseInput {
moveTo(Offset(30f, 40f))
}
mainClock.advanceTimeBy(TooltipDelayMillis + 1L)
onNodeWithTag("tooltip").assertExists()

onNodeWithTag("elementWithTooltip").performMouseInput {
Expand Down Expand Up @@ -122,6 +124,7 @@ internal class TooltipAreaTest {
onNodeWithTag("elementWithTooltip").performMouseInput {
moveTo(Offset(30f, 40f))
}
mainClock.advanceTimeBy(TooltipDelayMillis + 1L)

// Move into the tooltip, but still inside the area
onNodeWithTag("tooltip").let {
Expand Down Expand Up @@ -178,6 +181,7 @@ internal class TooltipAreaTest {
onNodeWithTag("elementWithTooltip").performMouseInput {
moveTo(Offset(30f, 40f))
}
mainClock.advanceTimeBy(TooltipDelayMillis + 1L)
onNodeWithTag("tooltip").assertExists()

onNodeWithTag("elementWithTooltip").performMouseInput {
Expand All @@ -189,14 +193,15 @@ internal class TooltipAreaTest {
release()
moveBy(Offset(10f, 10f))
}
mainClock.advanceTimeBy(TooltipDelayMillis + 1L)
onNodeWithTag("tooltip").assertExists()
}

@Composable
private fun SimpleTooltipArea(
areaSize: Dp = 100.dp,
tooltipSize: Dp = 20.dp,
delayMillis: Int = 500
delayMillis: Int = TooltipDelayMillis
) {
TooltipArea(
tooltip = {
Expand All @@ -207,4 +212,6 @@ internal class TooltipAreaTest {
Box(Modifier.size(areaSize).testTag("elementWithTooltip"))
}
}
}
}

private const val TooltipDelayMillis = 500
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import androidx.compose.ui.test.runSkikoComposeUiTest
import androidx.compose.ui.test.swipeUp
import androidx.compose.ui.unit.dp
import kotlin.test.Test
import kotlin.test.assertTrue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -359,8 +360,12 @@ class LazyColumnTest {
val itemIndexWhenInterrupting = state.firstVisibleItemIndex
val itemOffsetWhenInterrupting = state.firstVisibleItemScrollOffset

assertThat(itemIndexWhenInterrupting).isNotEqualTo(0)
assertThat(itemOffsetWhenInterrupting).isNotEqualTo(0)
// It's wrong to assert that both firstVisibleItemIndex and firstVisibleItemScrollOffset are
// not 0 because either one could just happen to be 0, even though the list was scrolled
// correctly.
// assertThat(itemIndexWhenInterrupting).isNotEqualTo(0)
// assertThat(itemOffsetWhenInterrupting).isNotEqualTo(0)
assertTrue((itemIndexWhenInterrupting > 0) || (itemOffsetWhenInterrupting > 0))

onNodeWithTag(LazyListTag)
.performTouchInput { down(center) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,57 @@

package androidx.compose.material

import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.runComposeUiTest
import kotlinx.coroutines.launch
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test


class SnackbarTest {
@get:Rule
val rule = createComposeRule()

@OptIn(ExperimentalTestApi::class)
@Test
fun testQueueing() {
fun testParallelQueueing() = runComposeUiTest {
var snackbarsShown = 0

rule.setContent {
val scope = rememberCoroutineScope()
mainClock.autoAdvance = false
setContent {
val state = remember { SnackbarHostState() }
SnackbarHost(state)

Scaffold(
snackbarHost = { SnackbarHost(state) }
) {
scope.launch {
(1..4).forEach {
LaunchedEffect(Unit) {
repeat(4) {
launch {
state.showSnackbar(it.toString())
snackbarsShown = it
snackbarsShown++
}
}
}
}

mainClock.advanceTimeBy(60_000) // Should be larger than 4 * SnackbarDuration.Short
assertEquals(4, snackbarsShown)
}

@OptIn(ExperimentalTestApi::class)
@Test
fun testSequentialQueueing() = runComposeUiTest {
var snackbarsShown = 0
mainClock.autoAdvance = false
setContent {
val state = remember { SnackbarHostState() }
SnackbarHost(state)

LaunchedEffect(Unit) {
repeat(4) {
state.showSnackbar(it.toString())
snackbarsShown++
}
}
}

mainClock.advanceTimeBy(60_000) // Should be larger than 4 * SnackbarDuration.Short
assertEquals(4, snackbarsShown)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
Expand All @@ -29,13 +33,15 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.dp
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.junit.Assert
import org.junit.Rule


Expand All @@ -50,7 +56,7 @@ class TestBasicsTest {

// See https://github.com/JetBrains/compose-multiplatform/issues/3117
@Test
fun recompositionCompletesBeforeSetContentReturns() = repeat(1000) {
fun recompositionCompletesBeforeSetContentReturns() = repeat(100) {
runSkikoComposeUiTest {
var globalValue by atomic(0)
setContent {
Expand Down Expand Up @@ -86,27 +92,6 @@ class TestBasicsTest {
assertTrue(clockAfter > clockBefore, "performClick did not advance the test clock")
}

@Test
fun advancingClockRunsRecomposition() {
rule.mainClock.autoAdvance = false

rule.setContent {
var text by remember { mutableStateOf("1") }
Text(text, modifier = Modifier.testTag("text"))

LaunchedEffect(Unit) {
delay(1_000)
text = "2"
}
}

rule.onNodeWithTag("text").assertTextEquals("1")
rule.mainClock.advanceTimeBy(999, ignoreFrameDuration = true)
rule.onNodeWithTag("text").assertTextEquals("1")
rule.mainClock.advanceTimeBy(1, ignoreFrameDuration = true)
rule.onNodeWithTag("text").assertTextEquals("2")
}

@Test
fun obtainingSemanticsNodeInteractionWaitsUntilIdle() {
var text by mutableStateOf("1")
Expand Down Expand Up @@ -181,4 +166,77 @@ class TestBasicsTest {
rule.unregisterIdlingResource(idlingResource)
test(expectedValue = "first")
}

@Test(timeout = 500)
fun infiniteLoopInLaunchedEffectDoesNotHang() = runComposeUiTest {
eymar marked this conversation as resolved.
Show resolved Hide resolved
setContent {
LaunchedEffect(Unit) {
while (true) {
delay(1000)
}
}
}
}

@Test(timeout = 500)
fun delayInLaunchedEffectIsExecutedAfterAdvancingClock() = runComposeUiTest {
var value = 0
mainClock.autoAdvance = false
setContent {
LaunchedEffect(Unit) {
repeat(5) {
delay(1000)
value = it+1
}
}
}

assertEquals(0, value)
mainClock.advanceTimeBy(999, ignoreFrameDuration = true)
assertEquals(0, value)
mainClock.advanceTimeBy(2, ignoreFrameDuration = true)
assertEquals(1, value)
mainClock.advanceTimeBy(2000, ignoreFrameDuration = true)
assertEquals(3, value)
}

@Test
fun advancingClockCausesRecompositions() = runComposeUiTest {
var value by mutableStateOf(0)
val compositionValues = mutableListOf<Int>()
setContent {
compositionValues.add(value)
val capturedValue = value
LaunchedEffect(capturedValue) {
delay(1000)
value = capturedValue + 1
}
}

Assert.assertEquals(0, value)
mainClock.advanceTimeBy(10000)
assertContentEquals(0..9, compositionValues)
}

@Test
fun launchedEffectsRunAfterComposition() = runComposeUiTest {
val actions = mutableListOf<String>()
setContent {
LaunchedEffect(Unit) {
actions.add("LaunchedEffect")
}
actions.add("Composition")
}

assertContentEquals(listOf("Composition", "LaunchedEffect"), actions)
}

@Test
fun waitForIdleDoesNotAdvanceClockIfAlreadyIdle() = runComposeUiTest {
setContent { }

val initialTime = mainClock.currentTime
waitForIdle()
assertEquals(initialTime, mainClock.currentTime)
}
}
Loading