Modernizes RepeatWhenAttachedTest.
This may or may not fix the cause for the flakiness of this test as seen in the attached bug. Fix: 274992372 Test: N/A Change-Id: I95e596e0a1ab08dde4589ef0f4693202f344dfca
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
|
||||
package com.android.systemui.lifecycle
|
||||
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -28,8 +27,16 @@ import com.android.systemui.util.Assert
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.DisposableHandle
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -41,9 +48,9 @@ import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
@RunWithLooper
|
||||
class RepeatWhenAttachedTest : SysuiTestCase() {
|
||||
|
||||
@JvmField @Rule val mockito = MockitoJUnit.rule()
|
||||
@@ -54,9 +61,13 @@ class RepeatWhenAttachedTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var block: Block
|
||||
private lateinit var attachListeners: MutableList<View.OnAttachStateChangeListener>
|
||||
private lateinit var testScope: TestScope
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val testDispatcher = StandardTestDispatcher()
|
||||
testScope = TestScope(testDispatcher)
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
Assert.setTestThread(Thread.currentThread())
|
||||
whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)
|
||||
whenever(view.windowVisibility).thenReturn(View.GONE)
|
||||
@@ -71,186 +82,218 @@ class RepeatWhenAttachedTest : SysuiTestCase() {
|
||||
block = Block()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun `repeatWhenAttached - enforces main thread`() = runBlockingTest {
|
||||
Assert.setTestThread(null)
|
||||
|
||||
repeatWhenAttached()
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun `repeatWhenAttached - dispose enforces main thread`() = runBlockingTest {
|
||||
val disposableHandle = repeatWhenAttached()
|
||||
Assert.setTestThread(null)
|
||||
fun `repeatWhenAttached - enforces main thread`() =
|
||||
testScope.runTest {
|
||||
Assert.setTestThread(null)
|
||||
|
||||
disposableHandle.dispose()
|
||||
}
|
||||
repeatWhenAttached()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun `repeatWhenAttached - dispose enforces main thread`() =
|
||||
testScope.runTest {
|
||||
val disposableHandle = repeatWhenAttached()
|
||||
Assert.setTestThread(null)
|
||||
|
||||
disposableHandle.dispose()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - view starts detached - runs block when attached`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
repeatWhenAttached()
|
||||
assertThat(block.invocationCount).isEqualTo(0)
|
||||
fun `repeatWhenAttached - view starts detached - runs block when attached`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
repeatWhenAttached()
|
||||
assertThat(block.invocationCount).isEqualTo(0)
|
||||
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
attachListeners.last().onViewAttachedToWindow(view)
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
attachListeners.last().onViewAttachedToWindow(view)
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - view already attached - immediately runs block`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
fun `repeatWhenAttached - view already attached - immediately runs block`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
|
||||
repeatWhenAttached()
|
||||
repeatWhenAttached()
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - starts visible without focus - STARTED`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
fun `repeatWhenAttached - starts visible without focus - STARTED`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
|
||||
repeatWhenAttached()
|
||||
repeatWhenAttached()
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.STARTED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.STARTED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - starts with focus but invisible - CREATED`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
fun `repeatWhenAttached - starts with focus but invisible - CREATED`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
|
||||
repeatWhenAttached()
|
||||
repeatWhenAttached()
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - starts visible and with focus - RESUMED`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
fun `repeatWhenAttached - starts visible and with focus - RESUMED`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
|
||||
repeatWhenAttached()
|
||||
repeatWhenAttached()
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.RESUMED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.RESUMED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - becomes visible without focus - STARTED`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
val listenerCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowVisibilityChangeListener(listenerCaptor.capture())
|
||||
fun `repeatWhenAttached - becomes visible without focus - STARTED`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
val listenerCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowVisibilityChangeListener(listenerCaptor.capture())
|
||||
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
listenerCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
listenerCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.STARTED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.STARTED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - gains focus but invisible - CREATED`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
val listenerCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowFocusChangeListener(listenerCaptor.capture())
|
||||
fun `repeatWhenAttached - gains focus but invisible - CREATED`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
val listenerCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowFocusChangeListener(listenerCaptor.capture())
|
||||
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
listenerCaptor.value.onWindowFocusChanged(true)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
listenerCaptor.value.onWindowFocusChanged(true)
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - becomes visible and gains focus - RESUMED`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
val visibleCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowVisibilityChangeListener(visibleCaptor.capture())
|
||||
val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowFocusChangeListener(focusCaptor.capture())
|
||||
fun `repeatWhenAttached - becomes visible and gains focus - RESUMED`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
val visibleCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowVisibilityChangeListener(visibleCaptor.capture())
|
||||
val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
|
||||
verify(viewTreeObserver).addOnWindowFocusChangeListener(focusCaptor.capture())
|
||||
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
visibleCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
focusCaptor.value.onWindowFocusChanged(true)
|
||||
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
|
||||
visibleCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
|
||||
whenever(view.hasWindowFocus()).thenReturn(true)
|
||||
focusCaptor.value.onWindowFocusChanged(true)
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.RESUMED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.RESUMED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - view gets detached - destroys the lifecycle`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
fun `repeatWhenAttached - view gets detached - destroys the lifecycle`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
attachListeners.last().onViewDetachedFromWindow(view)
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
attachListeners.last().onViewDetachedFromWindow(view)
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - view gets reattached - recreates a lifecycle`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
attachListeners.last().onViewDetachedFromWindow(view)
|
||||
fun `repeatWhenAttached - view gets reattached - recreates a lifecycle`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
repeatWhenAttached()
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
attachListeners.last().onViewDetachedFromWindow(view)
|
||||
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
attachListeners.last().onViewAttachedToWindow(view)
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
attachListeners.last().onViewAttachedToWindow(view)
|
||||
|
||||
assertThat(block.invocationCount).isEqualTo(2)
|
||||
assertThat(block.invocations[0].lifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
assertThat(block.invocations[1].lifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(block.invocationCount).isEqualTo(2)
|
||||
assertThat(block.invocations[0].lifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
assertThat(block.invocations[1].lifecycleState).isEqualTo(Lifecycle.State.CREATED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - dispose attached`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
val handle = repeatWhenAttached()
|
||||
fun `repeatWhenAttached - dispose attached`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
val handle = repeatWhenAttached()
|
||||
|
||||
handle.dispose()
|
||||
handle.dispose()
|
||||
|
||||
assertThat(attachListeners).isEmpty()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(attachListeners).isEmpty()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - dispose never attached`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
val handle = repeatWhenAttached()
|
||||
fun `repeatWhenAttached - dispose never attached`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(false)
|
||||
val handle = repeatWhenAttached()
|
||||
|
||||
handle.dispose()
|
||||
handle.dispose()
|
||||
|
||||
assertThat(attachListeners).isEmpty()
|
||||
assertThat(block.invocationCount).isEqualTo(0)
|
||||
}
|
||||
assertThat(attachListeners).isEmpty()
|
||||
assertThat(block.invocationCount).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `repeatWhenAttached - dispose previously attached now detached`() = runBlockingTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
val handle = repeatWhenAttached()
|
||||
attachListeners.last().onViewDetachedFromWindow(view)
|
||||
fun `repeatWhenAttached - dispose previously attached now detached`() =
|
||||
testScope.runTest {
|
||||
whenever(view.isAttachedToWindow).thenReturn(true)
|
||||
val handle = repeatWhenAttached()
|
||||
attachListeners.last().onViewDetachedFromWindow(view)
|
||||
|
||||
handle.dispose()
|
||||
handle.dispose()
|
||||
|
||||
assertThat(attachListeners).isEmpty()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
}
|
||||
runCurrent()
|
||||
assertThat(attachListeners).isEmpty()
|
||||
assertThat(block.invocationCount).isEqualTo(1)
|
||||
assertThat(block.latestLifecycleState).isEqualTo(Lifecycle.State.DESTROYED)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.repeatWhenAttached(): DisposableHandle {
|
||||
return view.repeatWhenAttached(
|
||||
|
||||
Reference in New Issue
Block a user