factorize CallViewUITestsMethods and update gitlab ci

This commit is contained in:
Quentin Monnier 2022-12-07 19:21:18 -05:00 committed by Sylvain Berfini
parent 908126425c
commit 054b4dce5a
8 changed files with 96 additions and 106 deletions

View file

@ -21,15 +21,10 @@ job-android:
- ./gradlew assembleDebug
- ./gradlew assembleRelease
after_script:
- ln -s ./app/build/outputs/apk/debug/linphone-android-debug-*.apk ./apk/debug
- ln -s ./app/build/outputs/apk/release/linphone-android-release-*.apk ./apk/release
artifacts:
paths:
- ./app/build
- ./apk/debug
- ./apk/release
when: always
expire_in: 2 hour

View file

@ -17,32 +17,38 @@ variables:
job-android-uitests:
stage: uitests
tags: [ "macmini-m1-bis-xcode13" ]
tags: [ "macos-xcode13" ]
dependencies:
- job-android
before_script:
- git submodule update --init --recursive
- ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "$android_system_image" > emulatorSystemImageInstallation.log
- echo no | ${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager --verbose create avd --force --name $emulator_name --package $android_system_image --tag google_$emulator_type --abi $system_architecture --device $emulator_device > emulatorCreation.log
- mkdir logs && mkdir reports && mkdir apks
- ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "$android_system_image" > logs/emulatorSystemImageInstallation.log
- echo no | ${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager --verbose create avd --force --name $emulator_name --package $android_system_image --tag google_$emulator_type --abi $system_architecture --device $emulator_device > logs/emulatorCreation.log
- ${ANDROID_HOME}/platform-tools/adb start-server
- ${ANDROID_HOME}/emulator/emulator -avd $emulator_name &
- .gitlab-ci-files/wait-for-android-emulator
- ${ANDROID_HOME}/platform-tools/adb logcat -d > logs/logcats.log
script:
- ./gradlew -Pandroid.testInstrumentationRunnerArguments.class=org.linphone.testsuites.CallTestSuite -PscreportAutoClose=true connectedAndroidTest --continue
- ./gradlew -Pandroid.testInstrumentationRunnerArguments.class=org.linphone.testsuites.CallTestSuite -PscreportAutoClose=true connectedAndroidTest --continue > logs/build.log
- ${ANDROID_HOME}/platform-tools/adb -s emulator-5554 emu kill
- ${ANDROID_HOME}/platform-tools/adb -s emulator-5554 emu kill
- ${ANDROID_HOME}/platform-tools/adb kill-server
after_script:
- mkdir results && mv app/build/reports/androidTests/connected/* results
- python3 .gitlab-ci-files/html2xml-report.py -p results
- mv app/build/reports/androidTests/connected/* reports
- mv app/build/outputs/apk/debug/linphone-android-debug-*.apk ./apks/debug
- mv app/build/outputs/apk/release/linphone-android-release-*.apk ./apks/release
- python3 .gitlab-ci-files/html2xml-report.py -p reports
artifacts:
paths:
- results/*
- reports/*
- logs/*
- apks/*
when: always
reports:
junit:

View file

@ -1,19 +1,24 @@
package org.linphone.call
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import java.util.*
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.linphone.R
import org.linphone.methods.*
import org.linphone.methods.UITestsScreenshots.takeScreenshot
import org.linphone.utils.AppUtils.Companion.getString
@RunWith(AndroidJUnit4::class)
class IncomingCallPushUITests {
lateinit var methods: CallViewUITestsMethods
val methods = CallViewUITestsMethods
@get:Rule
val screenshotsRule = ScreenshotsRule(true)
@ -24,7 +29,7 @@ class IncomingCallPushUITests {
@Before
fun setUp() {
UITestsUtils.testAppSetup()
methods = CallViewUITestsMethods()
methods.refreshAccountInfo()
takeScreenshot("dialer_view")
methods.startIncomingCall()
takeScreenshot("dialer_view", "incoming_call_push")
@ -43,29 +48,34 @@ class IncomingCallPushUITests {
@Test
fun testNoAnswerCallPush() {
methods.noAnswerCallFromPush()
methods.waitForCallNotification(false, 30.0)
takeScreenshot("dialer_view")
}
@Test
fun testClickOnCallPush() {
methods.openIncomingCallViewFromPush()
val time = Date().time
methods.onPushAction(getString(R.string.incoming_call_notification_title), UITestsView.incomingCallView)
methods.checkCallTime(onView(withId(R.id.incoming_call_timer)), time)
takeScreenshot("incoming_call_view")
methods.endCall()
methods.endCall(UITestsView.incomingCallView)
takeScreenshot("dialer_view")
}
@Test
fun testDeclineCallPush() {
methods.declineCallFromPush()
methods.onPushAction("Decline", null)
methods.waitForCallNotification(false, 5.0)
takeScreenshot("dialer_view")
}
@Test
fun testAnswerCallPush() {
methods.answerCallFromPush()
val time = Date().time
methods.onPushAction(getString(R.string.incoming_call_notification_answer_action_label), UITestsView.singleCallView)
methods.checkCallTime(onView(withId(R.id.active_call_timer)), time)
takeScreenshot("single_call_view")
methods.endCall()
methods.endCall(UITestsView.singleCallView)
takeScreenshot("dialer_view")
}
}

View file

@ -1,5 +1,8 @@
package org.linphone.call
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import org.junit.After
@ -7,13 +10,16 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.linphone.R
import org.linphone.methods.*
import org.linphone.methods.UITestsScreenshots.takeScreenshot
import org.linphone.methods.UITestsUtils.checkWithTimeout
import org.linphone.utils.AppUtils.Companion.getString
@RunWith(AndroidJUnit4::class)
class IncomingCallUITests {
lateinit var methods: CallViewUITestsMethods
val methods = CallViewUITestsMethods
@get:Rule
val screenshotsRule = ScreenshotsRule(true)
@ -24,10 +30,10 @@ class IncomingCallUITests {
@Before
fun setUp() {
UITestsUtils.testAppSetup()
methods = CallViewUITestsMethods()
methods.refreshAccountInfo()
takeScreenshot("dialer_view")
methods.startIncomingCall()
methods.openIncomingCallViewFromPush()
methods.onPushAction(getString(R.string.incoming_call_notification_title), UITestsView.incomingCallView)
takeScreenshot("incoming_call_view")
}
@ -38,27 +44,27 @@ class IncomingCallUITests {
@Test
fun testOpenIncomingCallView() {
methods.endCall()
methods.endCall(UITestsView.incomingCallView)
takeScreenshot("dialer_view")
}
@Test
fun testNoAnswerIncomingCallView() {
methods.noAnswerCallFromIncomingCall()
UITestsView.incomingCallView.checkWithTimeout(doesNotExist(), 30.0)
takeScreenshot("dialer_view")
}
@Test
fun testDeclineIncomingCallView() {
methods.declineCallFromIncomingCallView()
methods.onCallAction(R.id.hangup, UITestsView.incomingCallView, doesNotExist())
takeScreenshot("dialer_view")
}
@Test
fun testAcceptIncomingCallView() {
methods.answerCallFromIncomingCallView()
methods.onCallAction(R.id.answer, UITestsView.singleCallView, matches(isDisplayed()))
takeScreenshot("single_call_view")
methods.endCall()
methods.endCall(UITestsView.singleCallView)
takeScreenshot("dialer_view")
}
}

View file

@ -1,7 +1,8 @@
package org.linphone.call
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
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
@ -14,12 +15,13 @@ import org.junit.runner.RunWith
import org.linphone.R
import org.linphone.methods.*
import org.linphone.methods.UITestsScreenshots.takeScreenshot
import org.linphone.methods.UITestsUtils.checkWithTimeout
@RunWith(AndroidJUnit4::class)
@LargeTest
class OutgoingCallUITests {
lateinit var methods: CallViewUITestsMethods
val methods = CallViewUITestsMethods
@get:Rule
val screenshotsRule = ScreenshotsRule(true)
@ -30,7 +32,7 @@ class OutgoingCallUITests {
@Before
fun setUp() {
UITestsUtils.testAppSetup()
methods = CallViewUITestsMethods()
methods.refreshAccountInfo()
takeScreenshot("dialer_view")
methods.startOutgoingCall()
takeScreenshot("outgoing_call_view")
@ -43,21 +45,21 @@ class OutgoingCallUITests {
@Test
fun testViewDisplay() {
methods.endCall()
methods.endCall(UITestsView.outgoingCallView)
takeScreenshot("dialer_view", "declined")
}
@Test
fun testNoAnswer() {
methods.noAnswerCallFromOutgoingCall()
UITestsView.outgoingCallView.checkWithTimeout(doesNotExist(), 30.0)
takeScreenshot("dialer_view", "no_answer")
}
@Test
fun testToggleMute() {
Espresso.onView(withId(R.id.microphone)).perform(ViewActions.click())
onView(withId(R.id.microphone)).perform(click())
takeScreenshot("outgoing_call_view", "mute")
Espresso.onView(withId(R.id.microphone)).perform(ViewActions.click())
onView(withId(R.id.microphone)).perform(click())
takeScreenshot("outgoing_call_view")
methods.endCall()
takeScreenshot("dialer_view", "declined")
@ -65,9 +67,9 @@ class OutgoingCallUITests {
@Test
fun testToggleSpeaker() {
Espresso.onView(withId(R.id.speaker)).perform(ViewActions.click())
onView(withId(R.id.speaker)).perform(click())
takeScreenshot("outgoing_call_view", "speaker")
Espresso.onView(withId(R.id.speaker)).perform(ViewActions.click())
onView(withId(R.id.speaker)).perform(click())
takeScreenshot("outgoing_call_view")
methods.endCall()
takeScreenshot("dialer_view", "declined")
@ -75,7 +77,7 @@ class OutgoingCallUITests {
@Test
fun testCancel() {
methods.cancelCallFromOutgoingCallView()
methods.onCallAction(R.id.hangup, UITestsView.outgoingCallView, doesNotExist())
takeScreenshot("dialer_view")
}
}

View file

@ -5,6 +5,7 @@ import android.app.NotificationManager
import android.content.Context
import android.widget.Chronometer
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.action.ViewActions.typeText
@ -13,6 +14,7 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.*
import java.util.*
import kotlinx.coroutines.*
import org.linphone.R
import org.linphone.core.AuthInfo
@ -21,12 +23,17 @@ import org.linphone.methods.UITestsUtils.activityScenario
import org.linphone.methods.UITestsUtils.checkWithTimeout
import org.linphone.utils.AppUtils.Companion.getString
class CallViewUITestsMethods {
object CallViewUITestsMethods {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val manager = UITestsCoreManager.instance
val appAccountAuthInfo: AuthInfo = UITestsCoreManager.instance.appAccountAuthInfo
val ghostAccount: UITestsRegisteredLinphoneCore = UITestsCoreManager.instance.ghostAccounts[0]
var appAccountAuthInfo: AuthInfo = UITestsCoreManager.instance.appAccountAuthInfo
var ghostAccount: UITestsRegisteredLinphoneCore = UITestsCoreManager.instance.ghostAccounts[0]
fun refreshAccountInfo() {
appAccountAuthInfo = UITestsCoreManager.instance.appAccountAuthInfo
ghostAccount = UITestsCoreManager.instance.ghostAccounts[0]
}
fun startIncomingCall() {
if (ghostAccount.callState != Call.State.Released) { ghostAccount.terminateCall() }
@ -47,17 +54,18 @@ class CallViewUITestsMethods {
checkCallTime(onView(withId(R.id.outgoing_call_timer)))
}
fun endCall() {
fun endCall(currentView: ViewInteraction? = null) {
if (ghostAccount.callState == Call.State.Released) { return }
ghostAccount.terminateCall()
ghostAccount.waitForCallState(Call.State.Released, 5.0)
onView(withId(R.id.outgoing_call_layout)).checkWithTimeout(doesNotExist(), 5.0)
currentView?.checkWithTimeout(doesNotExist(), 5.0)
waitForCallNotification(false, 5.0)
}
fun checkCallTime(view: ViewInteraction) = runBlocking {
fun checkCallTime(view: ViewInteraction, launchTime: Long = Date().time) = runBlocking {
view.checkWithTimeout(matches(isDisplayed()), 2.0)
val firstValue = ((Date().time - launchTime) / 1000).toInt() + 1
launch(Dispatchers.Default) {
val timerArray = arrayListOf<Int>()
repeat(3) {
@ -69,76 +77,32 @@ class CallViewUITestsMethods {
}
assert(timerArray.distinct().size >= 2) { "[UITests] Call Time is not correctly incremented, less than 2 differents values are displayed in 3 seconds" }
assert(timerArray == timerArray.sorted()) { "[UITests] Call Time is not correctly incremented, it is not increasing" }
assert(timerArray.first() <= 4) { "[UITests] Call Time is not correctly initialized, it is more than 4 right after the start (found: ${timerArray.first()}))" }
assert(timerArray.first() <= firstValue + 3) { "[UITests] Call Time is not correctly initialized, it is at ${timerArray.first()}, $firstValue seconds after the start)" }
}
}
fun noAnswerCallFromPush() {
waitForCallNotification(false, 30.0)
}
fun declineCallFromPush() {
val declineLabel = "Decline" // getString(R.string.incoming_call_notification_hangup_action_label)
fun onPushAction(label: String, resultingView: ViewInteraction?, timeout: Double = 5.0) {
try {
val decline = device.findObject(By.textContains(declineLabel))
decline.click()
val button = device.findObject(By.textContains(label))
button.click()
} catch (e: java.lang.NullPointerException) {
throw AssertionError("[UITests] Enable to find the \"$declineLabel\" button in the incoming call notification")
throw AssertionError("[UITests] Enable to find the \"$label\" button in the incoming call notification")
}
waitForCallNotification(false, 5.0)
resultingView?.checkWithTimeout(matches(isDisplayed()), timeout)
}
fun answerCallFromPush() {
val answerLabel = getString(R.string.incoming_call_notification_answer_action_label)
try {
val answer = device.findObject(By.textContains(answerLabel))
answer.click()
} catch (e: java.lang.NullPointerException) {
throw AssertionError("[UITests] Enable to find the \"$answerLabel\" button in the incoming call notification")
}
waitForCallNotification(false, 5.0)
onView(withId(R.id.single_call_layout)).checkWithTimeout(matches(isDisplayed()), 5.0)
fun onCallAction(
id: Int,
resultingView: ViewInteraction?,
assertion: ViewAssertion,
timeout: Double = 5.0
) {
onView(withId(id)).checkWithTimeout(matches(isDisplayed()), timeout)
onView(withId(id)).perform(click())
resultingView?.checkWithTimeout(assertion, 5.0)
}
fun openIncomingCallViewFromPush() {
try {
val notif = device.findObject(By.textContains(getString(R.string.incoming_call_notification_title)))
notif.click()
} catch (e: java.lang.NullPointerException) {
throw AssertionError("[UITests] Enable to find the incoming call notification")
}
onView(withId(R.id.incoming_call_layout)).checkWithTimeout(matches(isDisplayed()), 5.0)
checkCallTime(onView(withId(R.id.incoming_call_timer)))
}
fun declineCallFromIncomingCallView() {
onView(withId(R.id.hangup)).checkWithTimeout(matches(isDisplayed()), 5.0)
onView(withId(R.id.hangup)).perform(click())
onView(withId(R.id.incoming_call_layout)).checkWithTimeout(doesNotExist(), 5.0)
}
fun answerCallFromIncomingCallView() {
onView(withId(R.id.answer)).checkWithTimeout(matches(isDisplayed()), 5.0)
onView(withId(R.id.answer)).perform(click())
onView(withId(R.id.single_call_layout)).checkWithTimeout(matches(isDisplayed()), 5.0)
}
fun cancelCallFromOutgoingCallView() {
onView(withId(R.id.hangup)).checkWithTimeout(matches(isDisplayed()), 5.0)
onView(withId(R.id.hangup)).perform(click())
onView(withId(R.id.outgoing_call_layout)).checkWithTimeout(doesNotExist(), 5.0)
}
fun noAnswerCallFromIncomingCall() {
onView(withId(R.id.incoming_call_layout)).checkWithTimeout(doesNotExist(), 30.0)
}
fun noAnswerCallFromOutgoingCall() {
onView(withId(R.id.outgoing_call_layout)).checkWithTimeout(doesNotExist(), 30.0)
}
private fun waitForCallNotification(exist: Boolean, timeout: Double) = runBlocking {
fun waitForCallNotification(exist: Boolean, timeout: Double) = runBlocking {
var result = !exist
val wait = launch(Dispatchers.Default) {
lateinit var activity: Activity
@ -153,7 +117,7 @@ class CallViewUITestsMethods {
result = false
}
if (manager.activeNotifications.isEmpty()) result = false
if (result == exist) { cancel() }
if (result == exist) cancel()
delay(100)
}
}

View file

@ -54,6 +54,13 @@ object LinphonePermissions {
).toTypedArray()
}
object UITestsView {
val dialerView = onView(withId(R.id.incoming_call_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))
}
object UITestsUtils {
private var mainActivityIntent = Intent(getApplicationContext(), MainActivity::class.java)