From e6a220c5af4d722167703b94b71a2bbf658a3b80 Mon Sep 17 00:00:00 2001 From: Quentin Monnier Date: Tue, 24 Jan 2023 17:42:59 -0600 Subject: [PATCH] adding Linphone Rules with retry option --- .../linphone/call/IncomingCallPushUITests.kt | 6 +- .../org/linphone/call/IncomingCallUITests.kt | 10 +-- .../org/linphone/call/OutgoingCallUITests.kt | 12 +-- .../methods/CallViewUITestsMethods.kt | 8 +- .../java/org/linphone/methods/UITestsUtils.kt | 81 +++++++++++++++---- 5 files changed, 78 insertions(+), 39 deletions(-) diff --git a/app/src/androidTest/java/org/linphone/call/IncomingCallPushUITests.kt b/app/src/androidTest/java/org/linphone/call/IncomingCallPushUITests.kt index 752d49a58..ca200bc77 100644 --- a/app/src/androidTest/java/org/linphone/call/IncomingCallPushUITests.kt +++ b/app/src/androidTest/java/org/linphone/call/IncomingCallPushUITests.kt @@ -2,7 +2,6 @@ package org.linphone.call import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest -import androidx.test.rule.GrantPermissionRule import org.junit.After import org.junit.Before import org.junit.Rule @@ -20,10 +19,7 @@ class IncomingCallPushUITests { val methods = CallViewUITestsMethods @get:Rule - val screenshotsRule = ScreenshotsRule(true) - - @get:Rule - var mGrantPermissionRule = GrantPermissionRule.grant(*LinphonePermissions.CALL) + val linphoneUITestRule = LinphoneUITestRule(LinphonePermissions.CALL, true, 2) @Before fun setUp() { diff --git a/app/src/androidTest/java/org/linphone/call/IncomingCallUITests.kt b/app/src/androidTest/java/org/linphone/call/IncomingCallUITests.kt index 3ca9ebcf4..63f86ecd6 100644 --- a/app/src/androidTest/java/org/linphone/call/IncomingCallUITests.kt +++ b/app/src/androidTest/java/org/linphone/call/IncomingCallUITests.kt @@ -7,7 +7,6 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest -import androidx.test.rule.GrantPermissionRule import java.util.* import org.junit.After import org.junit.Before @@ -25,13 +24,9 @@ import org.linphone.utils.AppUtils.Companion.getString class IncomingCallUITests { val methods = CallViewUITestsMethods - var time = Date().time // to pass time value between setup and test function for call timer check @get:Rule - val screenshotsRule = ScreenshotsRule(true) - - @get:Rule - var mGrantPermissionRule = GrantPermissionRule.grant(*LinphonePermissions.CALL) + val linphoneUITestRule = LinphoneUITestRule(LinphonePermissions.CALL, true, 2) @Before fun setUp() { @@ -39,7 +34,6 @@ class IncomingCallUITests { methods.refreshAccountInfo() takeScreenshot("dialer_view") methods.startIncomingCall() - time = Date().time methods.onPushAction(getString(R.string.incoming_call_notification_title), UITestsView.incomingCallView) takeScreenshot("incoming_call_view") } @@ -51,7 +45,7 @@ class IncomingCallUITests { @Test fun testViewDisplay() { - methods.checkCallTime(onView(ViewMatchers.withId(R.id.outgoing_call_timer)), time) + methods.checkCallTime(onView(ViewMatchers.withId(R.id.incoming_call_timer)), methods.startCallTime) methods.endCall(UITestsView.incomingCallView) takeScreenshot("dialer_view") } diff --git a/app/src/androidTest/java/org/linphone/call/OutgoingCallUITests.kt b/app/src/androidTest/java/org/linphone/call/OutgoingCallUITests.kt index 878672989..0a2db3696 100644 --- a/app/src/androidTest/java/org/linphone/call/OutgoingCallUITests.kt +++ b/app/src/androidTest/java/org/linphone/call/OutgoingCallUITests.kt @@ -6,7 +6,6 @@ import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest -import androidx.test.rule.GrantPermissionRule import org.junit.After import org.junit.Before import org.junit.Rule @@ -24,10 +23,7 @@ class OutgoingCallUITests { val methods = CallViewUITestsMethods @get:Rule - val screenshotsRule = ScreenshotsRule(true) - - @get:Rule - var mGrantPermissionRule = GrantPermissionRule.grant(*LinphonePermissions.CALL) + val linphoneUITestRule = LinphoneUITestRule(LinphonePermissions.CALL, true, 2) @Before fun setUp() { @@ -45,7 +41,7 @@ class OutgoingCallUITests { @Test fun testViewDisplay() { - methods.checkCallTime(onView(withId(R.id.outgoing_call_timer))) + methods.checkCallTime(onView(withId(R.id.outgoing_call_timer)), methods.startCallTime) methods.endCall(UITestsView.outgoingCallView) takeScreenshot("dialer_view", "declined") } @@ -62,7 +58,7 @@ class OutgoingCallUITests { takeScreenshot("outgoing_call_view", "mute") onView(withId(R.id.microphone)).perform(click()) takeScreenshot("outgoing_call_view") - methods.endCall() + methods.endCall(UITestsView.outgoingCallView) takeScreenshot("dialer_view", "declined") } @@ -72,7 +68,7 @@ class OutgoingCallUITests { takeScreenshot("outgoing_call_view", "speaker") onView(withId(R.id.speaker)).perform(click()) takeScreenshot("outgoing_call_view") - methods.endCall() + methods.endCall(UITestsView.outgoingCallView) takeScreenshot("dialer_view", "declined") } diff --git a/app/src/androidTest/java/org/linphone/methods/CallViewUITestsMethods.kt b/app/src/androidTest/java/org/linphone/methods/CallViewUITestsMethods.kt index 724eda82a..999e38bc4 100644 --- a/app/src/androidTest/java/org/linphone/methods/CallViewUITestsMethods.kt +++ b/app/src/androidTest/java/org/linphone/methods/CallViewUITestsMethods.kt @@ -29,6 +29,7 @@ object CallViewUITestsMethods { val manager = UITestsCoreManager.instance var appAccountAuthInfo: AuthInfo = UITestsCoreManager.instance.appAccountAuthInfo var ghostAccount: UITestsRegisteredLinphoneCore = UITestsCoreManager.instance.ghostAccounts[0] + var startCallTime = Date().time // for checkCallTime function fun refreshAccountInfo() { appAccountAuthInfo = UITestsCoreManager.instance.appAccountAuthInfo @@ -39,8 +40,9 @@ object CallViewUITestsMethods { if (ghostAccount.callState != Call.State.Released) { ghostAccount.terminateCall() } ghostAccount.startCall(manager.createAddress(appAccountAuthInfo)) - ghostAccount.waitForCallState(Call.State.OutgoingRinging, 5.0) + startCallTime = Date().time + ghostAccount.waitForCallState(Call.State.OutgoingRinging, 5.0) waitForCallNotification(true, 5.0) } @@ -49,8 +51,10 @@ object CallViewUITestsMethods { onView(withId(R.id.sip_uri_input)).perform(typeText(ghostAccount.mAuthInfo.username)) onView(withContentDescription(R.string.content_description_start_call)).perform(click()) + startCallTime = Date().time UITestsView.outgoingCallView.checkWithTimeout(matches(isDisplayed()), 5.0) + ghostAccount.waitForCallState(Call.State.IncomingReceived, 5.0) } fun endCall(currentView: ViewInteraction? = null) { @@ -63,7 +67,7 @@ object CallViewUITestsMethods { } fun checkCallTime(view: ViewInteraction, launchTime: Long = Date().time) = runBlocking { - view.checkWithTimeout(matches(isDisplayed()), 2.0) + view.checkWithTimeout(matches(isDisplayed()), 5.0) val firstValue = ((Date().time - launchTime) / 1000).toInt() + 1 val wait = launch(Dispatchers.Default) { val timerArray = arrayListOf() diff --git a/app/src/androidTest/java/org/linphone/methods/UITestsUtils.kt b/app/src/androidTest/java/org/linphone/methods/UITestsUtils.kt index b81b12ba2..ac91b684d 100644 --- a/app/src/androidTest/java/org/linphone/methods/UITestsUtils.kt +++ b/app/src/androidTest/java/org/linphone/methods/UITestsUtils.kt @@ -5,17 +5,22 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.Observer import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.espresso.* import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.ViewAssertion +import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import java.util.* import kotlinx.coroutines.* -import org.junit.rules.TestWatcher +import org.junit.AssumptionViolatedException +import org.junit.rules.TestRule import org.junit.runner.Description +import org.junit.runners.model.MultipleFailureException +import org.junit.runners.model.Statement import org.linphone.LinphoneApplication import org.linphone.R import org.linphone.activities.main.MainActivity @@ -25,18 +30,64 @@ import org.linphone.core.Factory import org.linphone.core.TransportType import org.linphone.core.tools.Log -class ScreenshotsRule(active: Boolean) : TestWatcher() { +class LinphoneUITestRule( + private val permissions: Array, + private val screenshots: Boolean, + private val maxAttempts: Int +) : TestRule { - val screenshotComparison = active + // @get: Rule + // var grantPermissionRule = GrantPermissionRule.grant(*permissions) - override fun starting(description: Description) { - super.starting(description) - UITestsScreenshots.screenshotComparison = screenshotComparison + private var attemptNumber = 1 + + fun onStart(description: Description) { + UITestsScreenshots.screenshotComparison = screenshots UITestsScreenshots.definePath(description.className, description.methodName, Date().time.toString()) - if (screenshotComparison && !UITestsScreenshots.defaultPath.isDirectory) { + if (screenshots && !UITestsScreenshots.defaultPath.isDirectory) { UITestsScreenshots.defaultPath.mkdirs() } } + + fun onFailure( + base: Statement, + description: Description, + e: Throwable, + errors: MutableList + ) { + if (attemptNumber <= maxAttempts) { + Log.e("[UITests] ${description.displayName} attempt $attemptNumber failed") + Log.e("[UITests] ${description.displayName} $e") + Log.e("[UITests] ${description.displayName} launch of an attempt ${++attemptNumber} ") + onStart(description) + base.evaluate() + } else { + errors.add(e) + } + } + + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + val errors: MutableList = ArrayList() + onStart(description) + try { + base.evaluate() + // on succeed + } catch (e: AssumptionViolatedException) { + errors.add(e) + // on skip + } catch (e: Throwable) { + onFailure(base, description, e, errors) + } finally { + // on finish + } + + MultipleFailureException.assertEmpty(errors) + } + } + } } object LinphonePermissions { @@ -55,7 +106,7 @@ object LinphonePermissions { } object UITestsView { - val dialerView = onView(withId(R.id.incoming_call_layout)) + val dialerView = onView(withId(R.id.dialer_layout)) val incomingCallView = onView(withId(R.id.incoming_call_layout)) val outgoingCallView = onView(withId(R.id.outgoing_call_layout)) val singleCallView = onView(withId(R.id.single_call_layout)) @@ -69,7 +120,7 @@ object UITestsUtils { fun testAppSetup() { // launch app Log.i("[UITests] Launch Linphone app") - if (!isAppLaunch()) { launchApp() } + launchApp() try { onView(withId(R.id.assistant_welcome_layout)).check(doesNotExist()) } catch (e: Throwable) { @@ -80,7 +131,7 @@ object UITestsUtils { connectAccount() assert(accountIsConnected()) { "registration state on the Status Bar is still not : Connected after 10 seconds" } } - onView(withId(R.id.dialer_layout)).checkWithTimeout(matches(isDisplayed()), 5.0) + UITestsView.dialerView.checkWithTimeout(matches(isDisplayed()), 5.0) } fun launchApp() { @@ -152,9 +203,8 @@ object UITestsUtils { } fun ViewInteraction.checkWithTimeout(viewAssert: ViewAssertion, timeout: Double): ViewInteraction = runBlocking { - val time = Date().time val wait = launch(Dispatchers.Default) { - repeat((timeout * 10).toInt()) { + repeat((timeout * 10).toInt()) { i -> try { check(viewAssert) cancel() @@ -165,7 +215,6 @@ object UITestsUtils { } } wait.join() - check { view, noViewFoundException -> Log.i("[UITests] $view (found in ${(Date().time - time).toFloat() / 1000} sec)") } - check(viewAssert) + check(viewAssert).withFailureHandler { error, viewMatcher -> throw Exception("[UITests] $error") } } }