Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
[Android] Improve reliability of UI tests running on CI emulator (#872)
Browse files Browse the repository at this point in the history
* Dismiss ANR in UI tests

* Retry test on failure
  • Loading branch information
jonnyandrew authored Nov 17, 2023
1 parent 97e3c85 commit 628b612
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 1 deletion.
1 change: 1 addition & 0 deletions platforms/android/library-compose/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ dependencies {
testImplementation libs.test.kotlin.coroutines
testImplementation libs.test.turbine
testImplementation libs.molecule.runtime
androidTestImplementation project(":test")
androidTestImplementation libs.test.androidx.junit
androidTestImplementation libs.test.androidx.espresso
androidTestImplementation libs.test.mockk.android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.element.android.wysiwyg.compose.testutils.ComposerActions
import io.element.android.wysiwyg.compose.testutils.StateFactory
import io.element.android.wysiwyg.compose.testutils.copy
import io.element.android.wysiwyg.compose.testutils.showContent
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.utils.NBSP
import io.element.android.wysiwyg.view.models.InlineFormat
import kotlinx.coroutines.test.runTest
Expand All @@ -19,6 +20,9 @@ class RichTextEditorActionsTest {
@get:Rule
val composeTestRule = createComposeRule()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

@Test
fun testBold() = runTest {
val state = StateFactory.createState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.utils.NBSP
import io.element.android.wysiwyg.view.models.InlineFormat
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -27,6 +28,9 @@ class RichTextEditorStateTest {
@get:Rule
val composeTestRule = createComposeRule()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

@Test
fun testSharingState() = runTest {
val state = RichTextEditorState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.element.android.wysiwyg.compose.testutils.StateFactory.createState
import io.element.android.wysiwyg.compose.testutils.ViewMatchers.isRichTextEditor
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Rule
Expand All @@ -22,6 +23,9 @@ class RichTextEditorStyleTest {
@get:Rule
val composeTestRule = createComposeRule()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

private val state = createState()
private val bulletRadius = MutableStateFlow(2.dp)
private val codeBgColor = MutableStateFlow(Color.Blue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.element.android.wysiwyg.compose.testutils.StateFactory.createState
import io.element.android.wysiwyg.compose.testutils.ViewMatchers.isRichTextEditor
import io.element.android.wysiwyg.compose.testutils.copy
import io.element.android.wysiwyg.compose.testutils.showContent
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.view.models.LinkAction
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
Expand All @@ -26,6 +27,9 @@ class RichTextEditorTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

@Test
fun testTypeText() {
val state = createState()
Expand Down
1 change: 1 addition & 0 deletions platforms/android/library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ dependencies {
testImplementation libs.test.robolectric
testImplementation libs.test.mockk
testImplementation libs.test.hamcrest
androidTestImplementation project(":test")
androidTestImplementation libs.test.androidx.junit
androidTestImplementation libs.test.androidx.espresso
androidTestImplementation libs.test.androidx.espresso.accessibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.wysiwyg.display.TextDisplay
import io.element.android.wysiwyg.test.R
import io.element.android.wysiwyg.test.rules.createFlakyEmulatorRule
import io.element.android.wysiwyg.test.utils.*
import io.element.android.wysiwyg.utils.RustErrorCollector
import io.element.android.wysiwyg.view.models.InlineFormat
Expand All @@ -54,6 +55,9 @@ class EditorEditTextInputTests {
@get:Rule
val scenarioRule = ActivityScenarioRule(TestActivity::class.java)

@get:Rule
val flakyEmulatorRule = createFlakyEmulatorRule()

private val ipsum = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."

init {
Expand Down
2 changes: 1 addition & 1 deletion platforms/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ dependencyResolutionManagement {
}
}
rootProject.name = "Rich Text Editor"
include ':example-view', ':example-compose', ':library', ':library-compose'
include ':example-view', ':example-compose', ':library', ':library-compose', ':test'
1 change: 1 addition & 0 deletions platforms/android/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
34 changes: 34 additions & 0 deletions platforms/android/test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "io.element.android.wysiwyg.test"
compileSdk = 34

defaultConfig {
minSdk = 21
}

buildTypes {
release {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

kotlin {
jvmToolchain(11)
}

dependencies {
implementation(libs.test.androidx.uiautomator)
implementation(libs.test.junit)
implementation(libs.test.androidx.espresso)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.element.android.wysiwyg.test.rules

import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject
import androidx.test.uiautomator.UiSelector
import org.junit.rules.TestWatcher
import org.junit.runner.Description

internal class DismissAnrRule : TestWatcher() {
override fun starting(description: Description) {
dismissAnr()
}
}

private fun dismissAnr() {
val device = UiDevice.getInstance(getInstrumentation())
val dialog = device.findAnrDialog()
if (dialog.exists()) {
device.findWaitButton().click()
}
}

private fun UiDevice.findAnrDialog(): UiObject =
findObject(UiSelector().textContains("isn't responding"))

private fun UiDevice.findWaitButton(): UiObject =
findObject(UiSelector().text("Wait").enabled(true))
.apply { waitForExists(5000) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.element.android.wysiwyg.test.rules

import org.junit.rules.RuleChain
import org.junit.rules.TestRule

/**
* Creates a rule that helps to reduce emulator related flakiness.
*/
fun createFlakyEmulatorRule(): TestRule = RuleChain
.outerRule(RetryOnFailureRule())
.around(DismissAnrRule())
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.element.android.wysiwyg.test.rules

import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

internal class RetryOnFailureRule : TestRule {
override fun apply(
base: Statement,
description: Description
): Statement =
RetryStatement(base)
}

private class RetryStatement(private val base: Statement) : Statement() {
override fun evaluate() {
try {
base.evaluate()
return
} catch (t: Throwable) {
base.evaluate()
}
}
}

0 comments on commit 628b612

Please sign in to comment.