diff --git a/configuration/src/main/kotlin/com/malinskiy/marathon/config/LogicalConfigurationValidator.kt b/configuration/src/main/kotlin/com/malinskiy/marathon/config/LogicalConfigurationValidator.kt index f734f7c6e..4eb016dd3 100644 --- a/configuration/src/main/kotlin/com/malinskiy/marathon/config/LogicalConfigurationValidator.kt +++ b/configuration/src/main/kotlin/com/malinskiy/marathon/config/LogicalConfigurationValidator.kt @@ -27,6 +27,9 @@ class LogicalConfigurationValidator : ConfigurationValidator { is VendorConfiguration.IOSConfiguration -> { configuration.vendorConfiguration.validate() } + is VendorConfiguration.AndroidConfiguration -> { + configuration.vendorConfiguration.validate() + } else -> Unit } diff --git a/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt b/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt index ce1c01dc0..946d92f1e 100644 --- a/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt +++ b/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt @@ -72,6 +72,26 @@ sealed class VendorConfiguration { @JsonProperty("disableWindowAnimation") val disableWindowAnimation: Boolean = DEFAULT_DISABLE_WINDOW_ANIMATION, ) : VendorConfiguration() { fun safeAndroidSdk(): File = androidSdk ?: throw ConfigurationException("No android SDK path specified") + + fun validate() { + validateFile(applicationOutput) + validateFile(testApplicationOutput) + splitApks?.forEach { validateFile(it) } + extraApplicationsOutput?.forEach { validateFile(it) } + outputs?.forEach { + validateFile(it.application) + validateFile(it.testApplication) + it.splitApks?.forEach { apk -> validateFile(apk) } + it.extraApplications?.forEach { apk -> validateFile(apk) } + } + } + + private fun validateFile(file: File?) { + if (file != null) { + if (!file.exists()) throw ConfigurationException("${file.absolutePath} does not exist") + if (!file.isFile) throw ConfigurationException("${file.absolutePath} must be a regular file") + } + } } class AndroidConfigurationBuilder { diff --git a/vendor/vendor-android/src/main/kotlin/com/malinskiy/marathon/android/adam/AdamDeviceProvider.kt b/vendor/vendor-android/src/main/kotlin/com/malinskiy/marathon/android/adam/AdamDeviceProvider.kt index c8c8f242d..b9268cb38 100644 --- a/vendor/vendor-android/src/main/kotlin/com/malinskiy/marathon/android/adam/AdamDeviceProvider.kt +++ b/vendor/vendor-android/src/main/kotlin/com/malinskiy/marathon/android/adam/AdamDeviceProvider.kt @@ -67,6 +67,7 @@ class AdamDeviceProvider( private val socketFactory = VertxSocketFactory(idleTimeout = vendorConfiguration.timeoutConfiguration.socketIdleTimeout.toMillis()) private val logcatManager: LogcatManager = LogcatManager() private lateinit var deviceEventsChannel: ReceiveChannel>> + private var deviceEventsChannelInitialized = false private val deviceEventsChannelMutex = Mutex() private val multiServerDeviceStateTracker = MultiServerDeviceStateTracker() @@ -132,6 +133,7 @@ class AdamDeviceProvider( } } } + deviceEventsChannelInitialized = true } for ((client, currentDeviceList) in deviceEventsChannel) { multiServerDeviceStateTracker.update(client, currentDeviceList).forEach { update -> @@ -160,6 +162,7 @@ class AdamDeviceProvider( devices[serial] = ProvidedDevice(device, job) } } + TrackingUpdate.DISCONNECTED -> { devices[serial]?.let { (device, job) -> if (job.isActive) { @@ -169,6 +172,7 @@ class AdamDeviceProvider( device.dispose() } } + TrackingUpdate.NOTHING_TO_DO -> Unit } logger.debug { "Device $serial changed state to $state" } @@ -180,7 +184,7 @@ class AdamDeviceProvider( override suspend fun borrow(): AdamAndroidDevice { var availableDevices = devices.filter { it.value.setupJob.isCompleted && !it.value.setupJob.isCancelled } - while(availableDevices.isEmpty()) { + while (availableDevices.isEmpty()) { delay(200) availableDevices = devices.filter { it.value.setupJob.isCompleted && !it.value.setupJob.isCancelled } } @@ -198,7 +202,9 @@ class AdamDeviceProvider( providerJob?.cancel() channel.close() deviceEventsChannelMutex.withLock { - deviceEventsChannel.cancel() + if (deviceEventsChannelInitialized) { + deviceEventsChannel.cancel() + } } logcatManager.close() socketFactory.close()