Skip to content

Commit

Permalink
fix: retry for connection and not installation
Browse files Browse the repository at this point in the history
  • Loading branch information
amanjeetsingh150 committed Nov 16, 2024
1 parent 78f8d0c commit 4e88f87
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 51 deletions.
2 changes: 2 additions & 0 deletions maestro-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ dependencies {
api(libs.jackson.module.kotlin)
api(libs.jackson.dataformat.yaml)
api(libs.jackson.dataformat.xml)
api(libs.kotlin.result)
api(libs.kotlin.retry)
api(libs.apk.parser)

implementation(project(":maestro-ios"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ class XCTestDriverClient(
this.addInterceptor(it)
}
}
.addRetryInterceptor()
.addRetryAndShutdownInterceptor()
.build()

class XCTestDriverUnreachable(message: String) : IOException(message)
Expand Down Expand Up @@ -272,12 +270,6 @@ class XCTestDriverClient(
responseBodyAsString
)
}
code == NetworkErrorHandler.NO_RETRY_RESPONSE_CODE -> {
logger.error("Request for $pathString failed, because of XCUITest server got crashed/exit, body: $responseBodyAsString")
throw XCUITestServerError.NetworkError(
"Request for $pathString failed, because of XCUITest server got crashed/exit, body: $responseBodyAsString"
)
}
error.errorMessage.contains("Lost connection to the application.*".toRegex()) -> {
logger.error("Request for $pathString failed, because of app crash, body: $responseBodyAsString")
throw XCUITestServerError.AppCrash(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import com.github.michaelbull.retry.policy.retryIf
import maestro.utils.MaestroTimer
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import kotlinx.coroutines.runBlocking
import okhttp3.Request
import okio.buffer
import com.github.michaelbull.retry.retry
import kotlinx.coroutines.delay
import okio.sink
import okio.source
import kotlinx.coroutines.runBlocking
import org.apache.commons.io.FileUtils
import org.rauschig.jarchivelib.ArchiverFactory
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -54,6 +55,8 @@ class LocalXCTestInstaller(
return false
}

if (!isChannelAlive()) return false

fun killXCTestRunnerProcess() {
logger.trace("Will attempt to stop all alive XCTest Runner processes before uninstalling")

Expand All @@ -77,7 +80,6 @@ class LocalXCTestInstaller(
killXCTestRunnerProcess()

logger.trace("Uninstalling XCTest Runner from device $deviceId")
XCRunnerCLIUtils.uninstall(UI_TEST_RUNNER_APP_BUNDLE_ID, deviceId)
return true
}

Expand All @@ -97,21 +99,21 @@ class LocalXCTestInstaller(
throw IllegalStateException("XCTest was not started manually")
}

uninstall()

val result = runBlocking {
retry(limitAttempts(5) + retryException + binaryExponentialBackoff(base = 50L, max = 5000L)) {
logger.info("[Start] Install XCUITest runner on $deviceId")
startXCTestRunner()
logger.info("[Done] Install XCUITest runner on $deviceId")

val isDriverAlive = isChannelAlive()

if (!isDriverAlive) {
FileUtils.cleanDirectory(File(tempDir))
uninstall()
logger.info("Retrying installation of driver")
throw RetryDriverInstallationAction("iOS driver not ready")
logger.info("[Start] Install XCUITest runner on $deviceId")
startXCTestRunner()
logger.info("[Done] Install XCUITest runner on $deviceId")

retry(
limitAttempts(3) + retryException + binaryExponentialBackoff(
base = 1000L,
max = 120000L
)
) {
if (!isChannelAlive()) {
logger.info("Waiting for driver to become ready")
throw RetryDriverInstallationAction("iOS driver not ready after starting XCTest runner")
}

return@retry XCTestClient(host, defaultPort)
Expand All @@ -121,10 +123,8 @@ class LocalXCTestInstaller(
return result
}


override fun isChannelAlive(): Boolean {
val appAlive = XCRunnerCLIUtils.isAppAlive(UI_TEST_RUNNER_APP_BUNDLE_ID, deviceId)
return appAlive && xcTestDriverStatusCheck()
return xcTestDriverStatusCheck()
}

private fun ensureOpen(): Boolean {
Expand All @@ -151,16 +151,17 @@ class LocalXCTestInstaller(
xctestAPIBuilder("status")
.build()
}
val request by lazy { Request.Builder()
.get()
.url(url)
.build()
val request by lazy {
Request.Builder()
.get()
.url(url)
.build()
}

val okHttpClient by lazy {
OkHttpClient.Builder()
.connectTimeout(40, TimeUnit.SECONDS)
.readTimeout(100, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS)
.readTimeout(2, TimeUnit.SECONDS)
.build()
}
val checkSuccessful = try {
Expand All @@ -177,31 +178,24 @@ class LocalXCTestInstaller(
}

private fun startXCTestRunner() {
val processOutput = ProcessBuilder(listOf("xcrun", "simctl", "spawn", deviceId, "launchctl", "list"))
.start()
.inputStream.source().buffer().readUtf8()
.trim()
if (isChannelAlive()) {
logger.info("UI Test runner already running, returning")
return
}

logger.info("[Start] Writing xctest run file")
val tempDir = File(tempDir).apply { mkdir() }
val xctestRunFile = File("$tempDir/maestro-driver-ios-config.xctestrun")
writeFileToDestination(XCTEST_RUN_PATH, xctestRunFile)
logger.info("[Done] Writing xctest run file")

if (processOutput.contains(UI_TEST_RUNNER_APP_BUNDLE_ID)) {
logger.info("UI Test runner already running, stopping it")
uninstall()
} else {
logger.info("Not able to find ui test runner app running, going to install now")

logger.info("[Start] Writing maestro-driver-iosUITests-Runner app")
extractZipToApp("maestro-driver-iosUITests-Runner", UI_TEST_RUNNER_PATH)
logger.info("[Done] Writing maestro-driver-iosUITests-Runner app")
logger.info("[Start] Writing maestro-driver-iosUITests-Runner app")
extractZipToApp("maestro-driver-iosUITests-Runner", UI_TEST_RUNNER_PATH)
logger.info("[Done] Writing maestro-driver-iosUITests-Runner app")

logger.info("[Start] Writing maestro-driver-ios app")
extractZipToApp("maestro-driver-ios", UI_TEST_HOST_PATH)
logger.info("[Done] Writing maestro-driver-ios app")
}
logger.info("[Start] Writing maestro-driver-ios app")
extractZipToApp("maestro-driver-ios", UI_TEST_HOST_PATH)
logger.info("[Done] Writing maestro-driver-ios app")

logger.info("[Start] Running XcUITest with `xcodebuild test-without-building`")
xcTestProcess = XCRunnerCLIUtils.runXcTestWithoutBuild(
Expand All @@ -210,6 +204,9 @@ class LocalXCTestInstaller(
port = defaultPort,
enableXCTestOutputFileLogging = enableXCTestOutputFileLogging,
)
runBlocking {
delay(2000)
}
logger.info("[Done] Running XcUITest with `xcodebuild test-without-building`")
}

Expand All @@ -221,6 +218,7 @@ class LocalXCTestInstaller(
logger.info("[Start] Cleaning up the ui test runner files")
FileUtils.cleanDirectory(File(tempDir))
uninstall()
XCRunnerCLIUtils.uninstall(UI_TEST_RUNNER_APP_BUNDLE_ID, deviceId)
logger.info("[Done] Cleaning up the ui test runner files")
}

Expand All @@ -246,8 +244,10 @@ class LocalXCTestInstaller(
private const val UI_TEST_RUNNER_PATH = "/maestro-driver-iosUITests-Runner.zip"
private const val XCTEST_RUN_PATH = "/maestro-driver-ios-config.xctestrun"
private const val UI_TEST_HOST_PATH = "/maestro-driver-ios.zip"
private const val UI_TEST_RUNNER_APP_BUNDLE_ID = "dev.mobile.maestro-driver-iosUITests.xctrunner"
private const val UI_TEST_RUNNER_APP_BUNDLE_ID =
"dev.mobile.maestro-driver-iosUITests.xctrunner"
}

class RetryDriverInstallationAction(message: String, cause: Throwable? = null): Exception(message, cause)
class RetryDriverInstallationAction(message: String, cause: Throwable? = null) :
Exception(message, cause)
}
1 change: 1 addition & 0 deletions maestro-ios/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
implementation(project(":maestro-ios-driver"))

implementation(libs.kotlin.result)
implementation(libs.kotlin.retry)
implementation(libs.slf4j)
implementation(libs.square.okio)
api(libs.google.gson)
Expand Down

0 comments on commit 4e88f87

Please sign in to comment.