diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 4c8126fbf..f67f41fea 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -25,13 +25,11 @@ dependencies { implementation(libs.apache.commons.io) implementation(libs.apache.commons.text) implementation(libs.gson) - implementation(libs.kotlin.logging) implementation(libs.kotlinx.coroutines.core) implementation(libs.ktor.client.core) implementation(libs.ktor.client.auth) implementation(libs.ktor.client.apache) implementation(libs.jackson.annotations) - implementation(libs.logback.classic) implementation(libs.slf4j.api) api(libs.koin.core) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt b/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt index 93524d308..12466e31c 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt @@ -18,7 +18,6 @@ import com.malinskiy.marathon.execution.StrictRunProcessor import com.malinskiy.marathon.execution.TestParser import com.malinskiy.marathon.execution.TestShard import com.malinskiy.marathon.execution.progress.ProgressReporter -import com.malinskiy.marathon.log.MarathonLogConfigurator import com.malinskiy.marathon.log.MarathonLogging import com.malinskiy.marathon.report.logs.LogsProvider import com.malinskiy.marathon.test.Test @@ -38,13 +37,12 @@ class Marathon( private val cachedTestsReporter: CacheTestReporter, private val progressReporter: ProgressReporter, private val strictRunChecker: StrictRunChecker, - private val logConfigurator: MarathonLogConfigurator, private val logsProvider: LogsProvider, private val track: Track, private val timer: Timer ) : MarathonRunner { - private val logger = MarathonLogging.logger("Marathon") + private val logger = MarathonLogging.getLogger(Marathon::class.java) private val configurationValidator = LogicalConfigurationValidator() private val strictRunProcessor = StrictRunProcessor(configuration.strictRunConfiguration) @@ -54,14 +52,18 @@ class Marathon( override suspend fun start() { logger.debug("Starting Marathon") - MarathonLogging.debug = configuration.debug - logConfigurator.configure() + configurationValidator.validate(configuration) + logger.debug("Initializing device provider") deviceProvider.initialize() - logger.debug("Finished loading device provider") - configurationValidator.validate(configuration) + if (configuration.outputDir.exists()) { + logger.info("Cleaning output directory ${configuration.outputDir}") + configuration.outputDir.deleteRecursively() + } + configuration.outputDir.mkdirs() + logger.debug("Initializing scheduler") val currentCoroutineContext = coroutineContext scheduler = Scheduler( deviceProvider, @@ -77,15 +79,6 @@ class Marathon( timer, currentCoroutineContext ) - - logger.debug("Created scheduler") - - if (configuration.outputDir.exists()) { - logger.info("Output directory ${configuration.outputDir} already exists") - configuration.outputDir.deleteRecursively() - } - configuration.outputDir.mkdirs() - scheduler.initialize() } @@ -93,22 +86,21 @@ class Marathon( val parsedTests = testParser.extract(componentInfo) val tests = applyTestFilters(parsedTests) - logger.info("Scheduling ${tests.size} tests for $componentInfo") - logger.debug(tests.joinToString(", ") { it.toTestName() }) + logger.info("Scheduling {} tests for {} component: {}", tests.size, componentInfo.name, tests.joinToString(", ") { it.toTestName() }) val shard = prepareTestShard(tests, analytics) scheduler.addTests(shard) } override suspend fun stopAndWaitForCompletion(): Boolean { - logger.debug("Waiting for completion") + logger.debug("Waiting for test run to complete") try { scheduler.stopAndWaitForCompletion() onFinish() } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { // We don't want to catch these. If an exception was thrown, we should fail the execution - logger.error("Error occurred while finishing tests run", e) + logger.error("An error occurred while finishing test run", e) throw e } return progressReporter.aggregateResult() @@ -125,7 +117,7 @@ class Marathon( try { tracker.finish() } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - throw ReportGenerationException("Failed to generate test run report with exception", e) + throw ReportGenerationException("Failed to generate test run report", e) } } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/cache/gradle/GradleHttpCacheService.kt b/core/src/main/kotlin/com/malinskiy/marathon/cache/gradle/GradleHttpCacheService.kt index 5f08bbdc1..3eda1d249 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/cache/gradle/GradleHttpCacheService.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/cache/gradle/GradleHttpCacheService.kt @@ -29,7 +29,7 @@ class GradleHttpCacheService(private val configuration: RemoteCacheConfiguration private val httpClient = createClient() - private val logger = MarathonLogging.logger("GradleHttpCacheService") + private val logger = MarathonLogging.getLogger(GradleHttpCacheService::class.java) override suspend fun load(key: CacheKey, reader: CacheEntryReader): Boolean = withContext(Dispatchers.IO) { @@ -37,7 +37,7 @@ class GradleHttpCacheService(private val configuration: RemoteCacheConfiguration val response = httpClient.get(key.key) if (response.status != HttpStatusCode.OK) { if (response.status != HttpStatusCode.NotFound) { - logger.warn("Got response status when loading cache entry for ${key.key} : ${response.status}") + logger.warn("Got response status {} when loading cache entry for {}", response.status, key.key) } false } else { @@ -45,7 +45,7 @@ class GradleHttpCacheService(private val configuration: RemoteCacheConfiguration true } } catch (exception: IOException) { - logger.warn("Error during loading cache entry for ${key.key}", exception) + logger.warn("Error loading cache entry for {}", key.key, exception) false } } @@ -59,10 +59,10 @@ class GradleHttpCacheService(private val configuration: RemoteCacheConfiguration setBody(ByteArrayContent(stream.toByteArray())) } if (!response.status.isSuccess()) { - logger.warn("Got response status when storing cache entry for ${key.key} : ${response.status}") + logger.warn("Got response status {} when storing cache entry for {}", response.status, key.key) } } catch (exception: IOException) { - logger.warn("Error during storing cache entry for ${key.key}", exception) + logger.warn("Error storing cache entry for {}", key.key, exception) } finally { stream.close() } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheLoader.kt b/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheLoader.kt index 93dd13533..9e277ccb5 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheLoader.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheLoader.kt @@ -24,7 +24,7 @@ class TestCacheLoader( private val cacheKeyFactory: TestCacheKeyFactory ) { - private val logger = MarathonLogging.logger("TestCacheLoader") + private val logger = MarathonLogging.getLogger(TestCacheLoader::class.java) private val _results: Channel = unboundedChannel() val results: ReceiveChannel @@ -49,13 +49,11 @@ class TestCacheLoader( _results.send(result!!) } - logger.debug { - val hitOrMiss = when (result!!) { - is Hit -> "hit" - is Miss -> "miss" - } - "Cache $hitOrMiss for ${test.test.toSimpleSafeTestName()}, took $timeMillis milliseconds" + val hitOrMiss = when (result!!) { + is Hit -> "hit" + is Miss -> "miss" } + logger.debug("Cache {} for {} took {}ms", hitOrMiss, test.test.toSimpleSafeTestName(), timeMillis) } } } @@ -67,12 +65,12 @@ class TestCacheLoader( if (configuration.strictRunConfiguration.filter.matches(test)) { testCacheBlackList.add(test) } else { - testsToCheck.send(TestToCheck(poolId, test, isStrictRun = false)) + testsToCheck.send(TestToCheck(poolId, test)) } } if (testCacheBlackList.isNotEmpty()) { - logger.debug { "Cache miss for test in blacklist: ${testCacheBlackList.map { it.toSimpleSafeTestName() }} " } + logger.debug("Cache miss for tests in blacklist: {}", testCacheBlackList.map { it.toSimpleSafeTestName() }) _results.send(Miss(poolId, TestShard(testCacheBlackList))) } } else { @@ -84,8 +82,8 @@ class TestCacheLoader( testsToCheck.close() cacheCheckCompleted.await() _results.close() - logger.debug { "Cache loader is terminated" } + logger.debug("Cache loader is terminated") } - private class TestToCheck(val poolId: DevicePoolId, val test: Test, val isStrictRun: Boolean) + private class TestToCheck(val poolId: DevicePoolId, val test: Test) } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheSaver.kt b/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheSaver.kt index 6d01b9a74..6e59e7a5a 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheSaver.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestCacheSaver.kt @@ -16,7 +16,7 @@ class TestCacheSaver( private val testCacheKeyProvider: TestCacheKeyFactory ) { - private val logger = MarathonLogging.logger("TestCacheSaver") + private val logger = MarathonLogging.getLogger(TestCacheSaver::class.java) private val tasks: Channel = unboundedChannel() private lateinit var completableDeferred: Deferred @@ -38,7 +38,7 @@ class TestCacheSaver( suspend fun terminate() { tasks.close() completableDeferred.await() - logger.debug { "Cache saver is terminated" } + logger.debug("Cache saver is terminated") } private class SaveTask(val poolId: DevicePoolId, val result: TestResult) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestResultsCache.kt b/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestResultsCache.kt index e68138ae3..ab52e0f66 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestResultsCache.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/cache/test/TestResultsCache.kt @@ -18,7 +18,7 @@ class TestResultsCache( private val track: Track ) { - private val logger = MarathonLogging.logger("TestResultsCache") + private val logger = MarathonLogging.getLogger(TestResultsCache::class.java) suspend fun load(key: CacheKey, test: Test): TestResult? { val start = Instant.now() @@ -28,8 +28,8 @@ class TestResultsCache( return null } return reader.testResult - } catch (exception: Throwable) { - logger.warn("Error during loading cache entry for ${test.toSimpleSafeTestName()}", exception) + } catch (e: Throwable) { + logger.warn("Error during loading cache entry for {}", test.toSimpleSafeTestName(), e) return null } finally { val finish = Instant.now() @@ -42,8 +42,8 @@ class TestResultsCache( try { val writer = TestResultEntryWriter(testResult) cacheService.store(key, writer) - } catch (exception: Throwable) { - logger.warn("Error during storing cache entry for ${testResult.test.toSimpleSafeTestName()}", exception) + } catch (e: Throwable) { + logger.warn("Error during storing cache entry for {}", testResult.test.toSimpleSafeTestName(), e) } finally { val finish = Instant.now() track.cacheStore(start, finish, testResult.test) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/device/DevicePoolId.kt b/core/src/main/kotlin/com/malinskiy/marathon/device/DevicePoolId.kt index 1d1edd325..bef658979 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/device/DevicePoolId.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/device/DevicePoolId.kt @@ -1,3 +1,5 @@ package com.malinskiy.marathon.device -data class DevicePoolId(val name: String) +data class DevicePoolId(val name: String) { + override fun toString(): String = name +} diff --git a/core/src/main/kotlin/com/malinskiy/marathon/di/Modules.kt b/core/src/main/kotlin/com/malinskiy/marathon/di/Modules.kt index 04cd919d9..e0e079770 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/di/Modules.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/di/Modules.kt @@ -68,7 +68,7 @@ fun coreModule(timer: Timer?) = module { single { timer ?: SystemTimer(get()) } single { ProgressReporter(get()) } single { ConfigurationStrictRunChecker(get()) } - single { Marathon(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } + single { Marathon(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } } fun KoinApplication.marathonConfiguration(configuration: Configuration): KoinApplication { diff --git a/core/src/main/kotlin/com/malinskiy/marathon/exceptions/NoDevicesException.kt b/core/src/main/kotlin/com/malinskiy/marathon/exceptions/NoDevicesException.kt index 1fcceccb0..38c94bb44 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/exceptions/NoDevicesException.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/exceptions/NoDevicesException.kt @@ -1,3 +1,3 @@ package com.malinskiy.marathon.exceptions -class NoDevicesException(message: String) : RuntimeException(message) +class NoDevicesException(message: String = "No devices found") : RuntimeException(message) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/Configuration.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/Configuration.kt index e6c107c88..072379349 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/Configuration.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/Configuration.kt @@ -32,7 +32,6 @@ data class Configuration( val filteringConfiguration: FilteringConfiguration, val strictRunConfiguration: StrictRunConfiguration, - val debug: Boolean, val ignoreFailures: Boolean, val strictMode: Boolean, val uncompletedTestRetryQuota: Int, @@ -64,7 +63,6 @@ data class Configuration( filteringConfiguration: FilteringConfiguration?, strictRunConfiguration: StrictRunConfiguration?, - debug: Boolean?, ignoreFailures: Boolean?, strictMode: Boolean?, uncompletedTestRetryQuota: Int?, @@ -94,7 +92,6 @@ data class Configuration( retryStrategy = retryStrategy ?: NoRetryStrategy(), filteringConfiguration = filteringConfiguration ?: FilteringConfiguration(), strictRunConfiguration = strictRunConfiguration ?: StrictRunConfiguration(), - debug = debug ?: true, ignoreFailures = ignoreFailures ?: false, strictMode = strictMode ?: false, uncompletedTestRetryQuota = uncompletedTestRetryQuota ?: Integer.MAX_VALUE, @@ -122,7 +119,6 @@ data class Configuration( "retry" to retryStrategy.toString(), "filtering" to filteringConfiguration.toString(), "strictRun" to strictRunConfiguration.toString(), - "debug" to debug.toString(), "ignoreFailures" to ignoreFailures.toString(), "strictMode" to strictMode.toString(), "includeSerialRegexes" to includeSerialRegexes.toString(), diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/DevicePoolActor.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/DevicePoolActor.kt index 9f9c9ff37..c43d3aa55 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/DevicePoolActor.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/DevicePoolActor.kt @@ -41,7 +41,7 @@ class DevicePoolActor( ) : Actor(parent = parent, context = context) { - private val logger = MarathonLogging.logger("DevicePoolActor[${poolId.name}]") + private val logger = MarathonLogging.getLogger("DevicePoolActor[$poolId]") override suspend fun receive(msg: DevicePoolMessage) { when (msg) { @@ -82,7 +82,7 @@ class DevicePoolActor( private var noDevicesTimeoutDeferred: Deferred? = null private suspend fun notifyDevices() { - logger.debug { "Notify devices" } + logger.debug("Notifying devices") devices.values.forEach { it.safeSend(DeviceEvent.WakeUp) } @@ -146,19 +146,19 @@ class DevicePoolActor( } private suspend fun removeDevice(device: Device) { - logger.debug { "remove device ${device.serialNumber}" } + logger.debug("[{}] Removing from pool {}", device.serialNumber, poolId) val actor = devices.remove(device.serialNumber) actor?.safeSend(DeviceEvent.Terminate) - logger.debug { "devices.size = ${devices.size}" } + logger.debug("devices.size = {}", devices.size) if (noActiveDevices()) { if (!queue.stopRequested) return // we may receive new tests in the future noDevicesTimeoutDeferred?.cancel() - logger.debug { "scheduling terminating of device pool actor as no devices found" } + logger.debug("Scheduling termination of device pool actor as no devices found") noDevicesTimeoutDeferred = async(poolJob) { delay(TimeUnit.MINUTES.toMillis(NO_DEVICES_IN_POOL_TIMEOUT_MINUTES)) - logger.debug { "terminating device pool actor as no devices found after timeout" } + logger.debug("Terminating device pool actor as no devices found after timeout") terminate() } } @@ -170,11 +170,11 @@ class DevicePoolActor( private suspend fun addDevice(device: Device) { if (devices.containsKey(device.serialNumber)) { - logger.warn { "device ${device.serialNumber} already present in pool ${poolId.name}" } + logger.warn("[{}] Already present in pool {}. Skipping", device.serialNumber, poolId) return } - logger.debug { "add device ${device.serialNumber}" } + logger.debug("[{}] Adding to pool {}", device.serialNumber, poolId) noDevicesTimeoutDeferred?.cancel() diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/Scheduler.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/Scheduler.kt index 5f050fd33..dfca9bb20 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/Scheduler.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/Scheduler.kt @@ -35,7 +35,6 @@ import kotlin.coroutines.CoroutineContext * 1) Subscribe on DeviceProvider * 2) Create device pools using PoolingStrategy */ - class Scheduler( private val deviceProvider: DeviceProvider, private val cacheLoader: TestCacheLoader, @@ -55,22 +54,18 @@ class Scheduler( private val pools = ConcurrentHashMap>() private val poolingStrategy = configuration.poolingStrategy - private val logger = MarathonLogging.logger("Scheduler") + private val logger = MarathonLogging.getLogger(Scheduler::class.java) private val scope = CoroutineScope(context) suspend fun initialize() { - logger.debug { "Initializing scheduler" } - - subscribeOnDevices(job) - logger.debug { "Subscribed to devices" } + logger.debug("Subscribing to devices") + subscribeOnDevices() + logger.debug("Initializing test cache") subscribeToCacheController() - logger.debug { "Subscribed to cache controller" } - cacheLoader.initialize(scope) cacheSaver.initialize(scope) - logger.debug { "Initialized cache" } try { withTimeout(deviceProvider.deviceInitializationTimeoutMillis) { @@ -79,10 +74,10 @@ class Scheduler( } } } catch (e: TimeoutCancellationException) { - logger.debug("Timeout waiting for non-empty pools", e) + logger.warn("Timeout waiting for non-empty pools", e) job.cancelAndJoin() - throw NoDevicesException("") + throw NoDevicesException() } } @@ -90,7 +85,7 @@ class Scheduler( suspend fun stopAndWaitForCompletion() { cacheLoader.stop() - logger.debug { "Requesting stop in pools" } + logger.debug("Requesting stop in pools") pools.values.forEach { if (!it.isClosedForSend) { @@ -98,8 +93,6 @@ class Scheduler( } } - logger.debug { "Stop requested in all pools" } - for (child in job.children) { child.join() } @@ -124,9 +117,9 @@ class Scheduler( } } - private fun subscribeOnDevices(job: Job) { + private fun subscribeOnDevices() { scope.launch { - logger.debug { "Reading messages from device provider" } + logger.debug("Reading messages from device provider") for (msg in deviceProvider.subscribe()) { when (msg) { @@ -139,18 +132,18 @@ class Scheduler( } } - logger.debug { "Finished reading messages from device provider" } + logger.debug("Finished reading messages from device provider") } } private suspend fun onDeviceDisconnected(item: DeviceProvider.DeviceEvent.DeviceDisconnected) { val device = item.device if (filteredByConfiguration(device)) { - logger.debug { "device ${device.serialNumber} is filtered out by configuration. skipping disconnect" } + logger.debug("[{}] Filtered out by configuration. Skipping disconnection", device.serialNumber) return } - logger.debug { "device ${device.serialNumber} disconnected" } + logger.debug("[{}] Disconnected", device.serialNumber) pools.values.forEach { it.send(RemoveDevice(device)) } @@ -163,20 +156,18 @@ class Scheduler( ) { val device = item.device if (filteredByConfiguration(device)) { - logger.debug { "device ${device.serialNumber} is filtered out by configuration. skipping" } + logger.debug("[{}] Filtered out by configuration. Skipping connection", device.serialNumber) return } val poolId = poolingStrategy.associate(device) - logger.debug { "device ${device.serialNumber} associated with poolId ${poolId.name}" } + logger.debug("[{}] Associated with pool {}", device.serialNumber, poolId) pools.computeIfAbsent(poolId) { id -> - logger.debug { "pool actor ${id.name} is being created" } + logger.debug("Creating pool actor {}", id) DevicePoolActor(id, configuration, analytics, progressReporter, track, timer, logsProvider, strictRunChecker, parent, context) } - pools[poolId]?.send(AddDevice(device)) ?: logger.debug { - "not sending the AddDevice event " + - "to device pool for ${device.serialNumber}" - } + pools[poolId]?.send(AddDevice(device)) + ?: logger.debug("[{}] Not sending AddDevice event to device pool {}", device.serialNumber, poolId) track.deviceConnected(poolId, device.toDeviceInfo()) } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/device/DeviceActor.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/device/DeviceActor.kt index 720329788..937f6c965 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/device/DeviceActor.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/device/DeviceActor.kt @@ -38,6 +38,7 @@ class DeviceActor( ) : Actor(parent = parent, context = context) { + private val logger = MarathonLogging.getLogger("DevicePool[$devicePoolId]_DeviceActor[${device.serialNumber}]") private val state = StateMachine.create { initialState(DeviceState.Connected) state { @@ -94,7 +95,7 @@ class DeviceActor( val validTransition = it as? StateMachine.Transition.Valid if (validTransition !is StateMachine.Transition.Valid) { if (it.event !is DeviceEvent.WakeUp) { - logger.error { "Invalid transition from ${it.fromState} event ${it.event}" } + logger.error("Invalid transition from {} event {}", it.fromState, it.event) } return@onTransition } @@ -125,7 +126,6 @@ class DeviceActor( } } } - private val logger = MarathonLogging.logger("DevicePool[${devicePoolId.name}]_DeviceActor[${device.serialNumber}]") val isAvailable: Boolean get() = !isClosedForSend && state.state == DeviceState.Ready @@ -157,7 +157,7 @@ class DeviceActor( private var job: Job? = null private fun initialize() { - logger.debug { "initialize ${device.serialNumber}" } + logger.debug("[{}] Initializing", device.serialNumber) job = async { try { withRetry(30, 10000) { @@ -167,34 +167,34 @@ class DeviceActor( } catch (e: CancellationException) { throw e } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { - logger.debug(e) { "device ${device.serialNumber} initialization failed. Retrying" } + logger.debug("[{}] Initialization failed. Retrying", device.serialNumber, e) throw e } } } state.transition(DeviceEvent.Complete) } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { - logger.error("Initialization failed", e) + logger.error("[{}] Initialization failed", device.serialNumber, e) state.transition(DeviceEvent.Terminate) } } } private fun executeBatch(batch: TestBatch, result: CompletableDeferred) { - logger.debug { "executeBatch ${device.serialNumber}" } + logger.debug("[{}] Executing batch", device.serialNumber) job = async { val start = Instant.now() try { device.execute(configuration, devicePoolId, batch, result, progressReporter) state.transition(DeviceEvent.Complete) } catch (e: CancellationException) { - logger.warn("Device execution has been cancelled", e) + logger.warn("[{}] Device execution has been cancelled", device.serialNumber, e) state.transition(DeviceEvent.Terminate) } catch (e: DeviceLostException) { - logger.error("Critical error during execution", e) + logger.error("[{}] Critical error during execution", device.serialNumber, e) state.transition(DeviceEvent.Terminate) } catch (e: TestBatchExecutionException) { - logger.warn("Test batch failed execution", e) + logger.warn("[{}] Test batch failed execution", device.serialNumber, e) pool.send( DevicePoolMessage.FromDevice.ReturnTestBatch( device, @@ -204,7 +204,7 @@ class DeviceActor( ) state.transition(DeviceEvent.Complete) } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - logger.error("Unknown vendor exception caught. Considering this a recoverable error", e) + logger.error("[{}] Unknown vendor exception caught. Considering this a recoverable error", device.serialNumber, e) pool.send( DevicePoolMessage.FromDevice.ReturnTestBatch( device, batch, "Unknown vendor exception caught. \n" + @@ -228,7 +228,7 @@ class DeviceActor( } private fun terminate() { - logger.debug { "terminate ${device.serialNumber}" } + logger.debug("[{}] Terminating", device.serialNumber) job?.cancel() close() } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/progress/ProgressReporter.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/progress/ProgressReporter.kt index 921e73650..074a0fb91 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/progress/ProgressReporter.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/progress/ProgressReporter.kt @@ -4,6 +4,7 @@ import com.malinskiy.marathon.device.DeviceInfo import com.malinskiy.marathon.device.DevicePoolId import com.malinskiy.marathon.execution.Configuration import com.malinskiy.marathon.execution.progress.tracker.PoolProgressTracker +import com.malinskiy.marathon.log.MarathonLogging import com.malinskiy.marathon.test.Test import com.malinskiy.marathon.test.toTestName import java.util.concurrent.ConcurrentHashMap @@ -12,6 +13,7 @@ import kotlin.math.roundToInt const val HUNDRED_PERCENT_IN_FLOAT: Float = 100.0f class ProgressReporter(private val configuration: Configuration) { + private val logger = MarathonLogging.getLogger(ProgressReporter::class.java) private val reporters = ConcurrentHashMap() private inline fun execute(poolId: DevicePoolId, f: (PoolProgressTracker) -> T): T { @@ -29,22 +31,22 @@ class ProgressReporter(private val configuration: Configuration) { fun testStarted(poolId: DevicePoolId, device: DeviceInfo, test: Test) { execute(poolId) { it.testStarted(test) } - println("${toPercent(progress(poolId))} | [${poolId.name}]-[${device.serialNumber}] ${test.toTestName()} started") + logger.info("{} | [{}] {} started", toPercent(progress(poolId)), device.serialNumber, test.toTestName()) } fun testFailed(poolId: DevicePoolId, device: DeviceInfo, test: Test) { execute(poolId) { it.testFailed(test) } - println("${toPercent(progress(poolId))} | [${poolId.name}]-[${device.serialNumber}] ${test.toTestName()} failed") + logger.info("{} | [{}] {} failed", toPercent(progress(poolId)), device.serialNumber, test.toTestName()) } fun testPassed(poolId: DevicePoolId, device: DeviceInfo, test: Test) { execute(poolId) { it.testPassed(test) } - println("${toPercent(progress(poolId))} | [${poolId.name}]-[${device.serialNumber}] ${test.toTestName()} ended") + logger.info("{} | [{}] {} ended", toPercent(progress(poolId)), device.serialNumber, test.toTestName()) } fun testIgnored(poolId: DevicePoolId, device: DeviceInfo, test: Test) { execute(poolId) { it.testIgnored(test) } - println("${toPercent(progress(poolId))} | [${poolId.name}]-[${device.serialNumber}] ${test.toTestName()} ignored") + logger.info("{} | [{}] {} ignored", toPercent(progress(poolId)), device.serialNumber, test.toTestName()) } fun aggregateResult(): Boolean = diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/QueueActor.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/QueueActor.kt index f2f820aa7..8e6fa170b 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/QueueActor.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/QueueActor.kt @@ -45,7 +45,7 @@ class QueueActor( ) : Actor(parent = poolJob, context = coroutineContext) { - private val logger = MarathonLogging.logger("QueueActor[$poolId]") + private val logger = MarathonLogging.getLogger("QueueActor[$poolId]") private val sorting = configuration.sortingStrategy @@ -88,7 +88,7 @@ class QueueActor( stopRequested = true if (queue.isEmpty() && activeBatches.isEmpty()) { - logger.debug { "Stop requested, queue is empty and no active batches present, terminating" } + logger.debug("Stop requested, queue is empty and no active batches present, terminating") terminate() } } @@ -108,12 +108,12 @@ class QueueActor( } private suspend fun onBatchCompleted(device: DeviceInfo, results: TestBatchResults) { - val updatedResults = updateUncompletedTests(results) + logger.debug("[{}] Handling test results", device.serialNumber) + val updatedResults = updateUncompletedTests(results) val finished = updatedResults.finished val failed = updatedResults.failed - logger.debug { "handle test results ${device.serialNumber}" } if (finished.isNotEmpty()) { handleFinishedTests(finished, device) } @@ -131,9 +131,10 @@ class QueueActor( private suspend fun updateUncompletedTests(results: TestBatchResults): TestBatchResults { val batchId = results.batchId + val device = results.device val batchLogs = logProvider.getBatchReport(batchId) ?: null .also { - logger.warn { "no logs for batch = $batchId" } + logger.warn("[{}] No logs for batch {}", device.serialNumber, batchId) } val (failedFromUncompleted, uncompleted) = results @@ -141,7 +142,11 @@ class QueueActor( .partitionFastFailures(batchLogs) failedFromUncompleted.forEach { - logger.warn { "uncompleted test run marked as failed for ${it.test.toTestName()} as error message matches to fast test failures" } + logger.warn( + "[{}] Uncompleted test run marked as failed for {} as error message matches to fast test failures", + device.serialNumber, + it.test.toTestName() + ) } val (newUncompleted, failed) = results @@ -149,7 +154,11 @@ class QueueActor( .partitionIgnoredFailures(batchLogs) newUncompleted.forEach { - logger.debug { "failed test run marked as uncompleted for ${it.test.toTestName()} as error message matches to ignored test failures" } + logger.debug( + "[{}] Failed test run marked as uncompleted for {} as error message matches to ignored test failures", + device.serialNumber, + it.test.toTestName() + ) } return results.copy( @@ -208,9 +217,11 @@ class QueueActor( val (uncompletedFailFastFailed, uncompletedCleaned) = uncompletedTests.partition { it.hasFailFastFailureStackTrace() } if (uncompletedFailFastFailed.isNotEmpty()) { - logger.debug { - "Uncompleted test failed because of stacktrace for ${uncompletedFailFastFailed.joinToString(separator = ", ") { it.test.toTestName() }}" - } + logger.debug( + "[{}] Uncompleted test failed because of stacktrace for {}", + device.serialNumber, + uncompletedFailFastFailed.joinToString(separator = ", ") { it.test.toTestName() } + ) val uncompletedToFailed = uncompletedFailFastFailed.map { it.copy(status = TestStatus.FAILURE) } @@ -228,7 +239,11 @@ class QueueActor( } if (uncompletedRetryQuotaExceeded.isNotEmpty()) { - logger.debug { "uncompletedRetryQuotaExceeded for ${uncompletedRetryQuotaExceeded.joinToString(separator = ", ") { it.test.toTestName() }}" } + logger.debug( + "[{}] Uncompleted test retry quota exceeded for {}", + device.serialNumber, + uncompletedRetryQuotaExceeded.joinToString(separator = ", ") { it.test.toTestName() } + ) val uncompletedToFailed = uncompletedRetryQuotaExceeded.map { it.copy(status = TestStatus.FAILURE) } @@ -248,18 +263,18 @@ class QueueActor( } private suspend fun onReturnBatch(device: DeviceInfo, batch: TestBatch) { - logger.debug { "onReturnBatch ${device.serialNumber}" } + logger.debug("[{}] Batch returned", device.serialNumber) val uncompletedTests = batch.tests val results = uncompletedTests.map { val currentTimeMillis = timer.currentTimeMillis() TestResult( - it, - device, - TestStatus.INCOMPLETE, - currentTimeMillis, - currentTimeMillis + 1, - batch.id + test = it, + device = device, + status = TestStatus.INCOMPLETE, + startTime = currentTimeMillis, + endTime = currentTimeMillis + 1, + batchId = batch.id ) } @@ -294,11 +309,8 @@ class QueueActor( } } - private suspend fun handleFailedTests( - failed: Collection, - device: DeviceInfo - ) { - logger.debug { "handle failed tests ${device.serialNumber}" } + private fun handleFailedTests(failed: Collection, device: DeviceInfo) { + logger.debug("[{}] Handling failed tests", device.serialNumber) val retryList = retry .process(poolId, failed, flakyTests) .filter { @@ -320,25 +332,22 @@ class QueueActor( } private suspend fun onRequestBatch(device: DeviceInfo) { - logger.debug { "request next batch for device ${device.serialNumber}" } + logger.debug("[{}] Requested next batch", device.serialNumber) val queueIsEmpty = queue.isEmpty() if (queue.isNotEmpty() && !activeBatches.containsKey(device.serialNumber)) { - logger.debug { "sending next batch for device ${device.serialNumber}" } + logger.debug("[{}] Sending next batch", device.serialNumber) sendBatch(device) return } if (queueIsEmpty && activeBatches.isEmpty()) { if (stopRequested) { - logger.debug { "queue is empty and stop requested, terminating ${device.serialNumber}" } + logger.debug("[{}] Queue is empty and stop requested. Terminating", device.serialNumber) terminate() } else { - logger.debug { "queue is empty and stop is not requested yet, no batches available for ${device.serialNumber}" } + logger.debug("[{}] Queue is empty and stop is not requested yet, no batches available", device.serialNumber) } } else if (queueIsEmpty) { - logger.debug { - "queue is empty but there are active batches present for " + - activeBatches.keys.joinToString { it } - } + logger.debug("[{}] Queue is empty but there are active batches present for {}", device.serialNumber, activeBatches.keys.joinToString { it }) } } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporter.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporter.kt index df2f8cbdc..77443c016 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporter.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporter.kt @@ -21,7 +21,7 @@ class TestResultReporter( private val tests: HashMap> = HashMap() - private val logger = MarathonLogging.logger("TestResultReporter") + private val logger = MarathonLogging.getLogger(TestResultReporter::class.java) @Suppress("LongMethod") private fun createState(initialCount: Int) = StateMachine.create { @@ -100,7 +100,7 @@ class TestResultReporter( } onTransition { if (it as? StateMachine.Transition.Valid !is StateMachine.Transition.Valid) { - logger.error { "from ${it.fromState} event ${it.event}" } + logger.error("Invalid transition from {} event {}", it.fromState, it.event) } trackTestTransition(poolId, it) } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/log/KLoggerDebug.kt b/core/src/main/kotlin/com/malinskiy/marathon/log/KLoggerDebug.kt deleted file mode 100644 index 1629e177c..000000000 --- a/core/src/main/kotlin/com/malinskiy/marathon/log/KLoggerDebug.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.malinskiy.marathon.log - -import mu.KLogger -import org.slf4j.Marker - -class KLoggerDebug(underlyingLogger: KLogger) : KLogger by underlyingLogger { - override fun debug(msg: () -> Any?) { - super.warn(msg) - } - - override fun debug(t: Throwable, msg: () -> Any?) { - super.warn(t, msg) - } - - override fun info(msg: () -> Any?) { - super.warn(msg) - } - - override fun info(t: Throwable, msg: () -> Any?) { - super.warn(t, msg) - } - - override fun trace(msg: () -> Any?) { - super.warn(msg) - } - - override fun trace(t: Throwable, msg: () -> Any?) { - super.warn(t, msg) - } - - override fun debug(format: String, vararg arguments: Any) { - warn(format, arguments) - } - - override fun debug(marker: Marker?, format: String?, vararg arguments: Any?) { - warn(marker, format, arguments) - } - - override fun debug(marker: Marker?, format: String?, arg: Any?) { - warn(marker, format, arg) - } - - override fun debug(format: String?, arg: Any?) { - warn(format, arg) - } - - override fun debug(marker: Marker?, msg: String?) { - warn(marker, msg) - } - - override fun debug(format: String?, arg1: Any?, arg2: Any?) { - warn(format, arg1, arg2) - } - - override fun debug(msg: String?, t: Throwable?) { - warn(msg, t) - } - - override fun debug(marker: Marker?, msg: String?, t: Throwable?) { - warn(marker, msg, t) - } - - override fun debug(msg: String?) { - warn(msg) - } - - override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - warn(marker, format, arg1, arg2) - } - - override fun info(marker: Marker?, msg: String?) { - warn(marker, msg) - } - - override fun info(format: String?, arg1: Any?, arg2: Any?) { - warn(format, arg1, arg2) - } - - override fun info(marker: Marker?, format: String?, vararg arguments: Any?) { - warn(marker, format, arguments) - } - - override fun info(marker: Marker?, msg: String?, t: Throwable?) { - warn(marker, msg, t) - } - - override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - warn(marker, format, arg1, arg2) - } - - override fun info(msg: String?) { - warn(msg) - } - - override fun info(format: String?, vararg arguments: Any?) { - warn(format, arguments) - } - - override fun info(msg: String?, t: Throwable?) { - warn(msg, t) - } - - override fun info(marker: Marker?, format: String?, arg: Any?) { - warn(marker, format, arg) - } - - override fun info(format: String?, arg: Any?) { - warn(format, arg) - } - - override fun trace(format: String?, arg1: Any?, arg2: Any?) { - warn(format, arg1, arg2) - } - - override fun trace(marker: Marker?, msg: String?, t: Throwable?) { - warn(marker, msg, t) - } - - override fun trace(msg: String?) { - warn(msg) - } - - override fun trace(format: String?, arg: Any?) { - warn(format, arg) - } - - override fun trace(marker: Marker?, format: String?, arg: Any?) { - warn(marker, format, arg) - } - - override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - warn(marker, format, arg1, arg2) - } - - override fun trace(marker: Marker?, format: String?, vararg argArray: Any?) { - warn(marker, format, argArray) - } - - override fun trace(msg: String?, t: Throwable?) { - warn(msg, t) - } - - override fun trace(marker: Marker?, msg: String?) { - warn(marker, msg) - } - - override fun trace(format: String?, vararg arguments: Any?) { - warn(format, arguments) - } -} diff --git a/core/src/main/kotlin/com/malinskiy/marathon/log/LifecycleLoggerWrapper.kt b/core/src/main/kotlin/com/malinskiy/marathon/log/LifecycleLoggerWrapper.kt new file mode 100644 index 000000000..153d4b479 --- /dev/null +++ b/core/src/main/kotlin/com/malinskiy/marathon/log/LifecycleLoggerWrapper.kt @@ -0,0 +1,81 @@ +package com.malinskiy.marathon.log + +import org.slf4j.Logger +import org.slf4j.Marker + +internal class LifecycleLoggerWrapper(underlyingLogger: Logger) : Logger by underlyingLogger { + override fun debug(format: String, vararg arguments: Any) { + info(LIFECYCLE, format, *arguments) + } + + override fun debug(format: String?, arg: Any?) { + info(LIFECYCLE, format, arg) + } + + override fun debug(format: String?, arg1: Any?, arg2: Any?) { + info(LIFECYCLE, format, arg1, arg2) + } + + override fun debug(msg: String?, t: Throwable?) { + info(LIFECYCLE, msg, t) + } + + override fun debug(msg: String?) { + info(LIFECYCLE, msg) + } + + override fun info(format: String?, arg1: Any?, arg2: Any?) { + info(LIFECYCLE, format, arg1, arg2) + } + + override fun info(msg: String?) { + info(LIFECYCLE, msg) + } + + override fun info(format: String?, vararg arguments: Any?) { + info(LIFECYCLE, format, *arguments) + } + + override fun info(msg: String?, t: Throwable?) { + info(LIFECYCLE, msg, t) + } + + override fun info(format: String?, arg: Any?) { + info(LIFECYCLE, format, arg) + } + + override fun trace(format: String?, arg1: Any?, arg2: Any?) { + info(LIFECYCLE, format, arg1, arg2) + } + + override fun trace(msg: String?) { + info(LIFECYCLE, msg) + } + + override fun trace(format: String?, arg: Any?) { + info(LIFECYCLE, format, arg) + } + + override fun trace(msg: String?, t: Throwable?) { + info(LIFECYCLE, msg, t) + } + + override fun trace(format: String?, vararg arguments: Any?) { + info(LIFECYCLE, format, *arguments) + } + + companion object { + private val LIFECYCLE = getGradleLifecycleMarker() + + private fun getGradleLifecycleMarker(): Marker? { + return try { + val gradleLogging = Class.forName("org.gradle.api.logging.Logging") + gradleLogging.getDeclaredField("LIFECYCLE").get(null) as Marker? + } catch (@Suppress("SwallowedException") e: ClassNotFoundException) { + null + } catch (@Suppress("SwallowedException") e: NoSuchFieldException) { + null + } + } + } +} diff --git a/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogConfigurator.kt b/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogConfigurator.kt deleted file mode 100644 index 4eae9f727..000000000 --- a/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogConfigurator.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.malinskiy.marathon.log - -interface MarathonLogConfigurator { - fun configure() -} diff --git a/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogging.kt b/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogging.kt index 7fe4b9593..b95e8f73e 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogging.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/log/MarathonLogging.kt @@ -1,52 +1,17 @@ package com.malinskiy.marathon.log -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import mu.KLogger -import mu.KotlinLogging +import org.slf4j.Logger +import org.slf4j.LoggerFactory object MarathonLogging { var debug = true - private var warningPrinted = false - fun logger(func: () -> Unit): KLogger = - logger(level = null, func = func) + fun getLogger(name: String): Logger = + LoggerFactory.getLogger(name).wrapIfNeeded() - fun logger(name: String): KLogger = - logger(level = null, name = name) + fun getLogger(clazz: Class<*>): Logger = + LoggerFactory.getLogger(clazz).wrapIfNeeded() - fun logger(level: Level?, func: () -> Unit): KLogger { - val logger = KotlinLogging.logger(func) - return changeInternalLogLevel(logger, level = level) - } - - fun logger(level: Level?, name: String): KLogger { - val logger = KotlinLogging.logger(name) - return changeInternalLogLevel(logger, level = level) - } - - private fun changeInternalLogLevel(logger: KLogger, level: Level?): KLogger { - val internalLogger = logger.underlyingLogger as? Logger - - if (internalLogger == null) { - if (debug && !warningPrinted) { - println( - "Can't change log level during runtime for " + - "${logger.underlyingLogger.javaClass.simpleName}. " + - "Please configure your logger separately. " + - "Wrapping the log and redirecting everything into warn for now" - ) - warningPrinted = true - } - return KLoggerDebug(logger) - } else { - internalLogger.level = level - ?: when { - debug -> Level.DEBUG - else -> Level.ERROR - } - } - - return logger - } + private fun Logger.wrapIfNeeded(): Logger = + if (debug) LifecycleLoggerWrapper(this) else this } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/report/logs/LogTest.kt b/core/src/main/kotlin/com/malinskiy/marathon/report/logs/LogTest.kt index 754225d74..6868f9ee5 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/report/logs/LogTest.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/report/logs/LogTest.kt @@ -6,7 +6,9 @@ data class LogTest( val pkg: String, val clazz: String, val method: String -) +) { + override fun toString(): String = "$pkg.$clazz#$method" +} fun Test.toLogTest(): LogTest = LogTest(pkg, clazz, method) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/report/stdout/StdoutReporter.kt b/core/src/main/kotlin/com/malinskiy/marathon/report/stdout/StdoutReporter.kt index 7b7cfd03b..1bcfc6b49 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/report/stdout/StdoutReporter.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/report/stdout/StdoutReporter.kt @@ -12,7 +12,7 @@ class StdoutReporter(private val timer: Timer) : Reporter { val cliReportBuilder = StringBuilder().appendLine("Marathon run finished:") summary.pools.forEach { poolSummary -> - cliReportBuilder.appendLine("Device pool ${poolSummary.poolId.name}:") + cliReportBuilder.appendLine("Device pool ${poolSummary.poolId}:") cliReportBuilder.appendLine( "\t${poolSummary.passed.size} passed, " + "${poolSummary.failed.size} failed, " + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 11cef415f..cfd8f3421 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,10 +16,8 @@ junit5 = "5.10.3" jsonassert = "1.5.0" koin = "2.0.1" kotlin = "2.0.20" -kotlin-logging = "1.4.9" kotlinx-coroutines = "1.9.0" ktor = "2.3.13" -logback = "1.2.3" marathon = "0.5.4" mockito = "5.4.0" slf4j = "1.7.36" @@ -51,7 +49,6 @@ koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" } kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" } kotlin-gradle-api = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlin" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } -kotlin-logging = { module = "io.github.microutils:kotlin-logging", version.ref = "kotlin-logging" } kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" } @@ -61,7 +58,6 @@ ktor-client-core = { module = "io.ktor:ktor-client-core" } ktor-client-auth = { module = "io.ktor:ktor-client-auth" } ktor-client-apache = { module = "io.ktor:ktor-client-apache" } ktor-client-mock = { module = "io.ktor:ktor-client-mock" } -logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } spek-api = { module = "org.jetbrains.spek:spek-api", version.ref = "spek" } diff --git a/marathon-gradle-plugin/build.gradle.kts b/marathon-gradle-plugin/build.gradle.kts index 015ef8504..ca12f25c8 100644 --- a/marathon-gradle-plugin/build.gradle.kts +++ b/marathon-gradle-plugin/build.gradle.kts @@ -20,6 +20,5 @@ dependencies { implementation(project(":vendor:vendor-android:ddmlib")) implementation(libs.android.gradle.api) implementation(libs.android.tools.common) - implementation(libs.kotlin.logging) implementation(libs.kotlinx.coroutines.core) } diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt index f4a072c3d..70dcffd45 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt @@ -21,7 +21,6 @@ internal fun createCommonConfiguration( retryStrategy = extensionConfig.retryStrategy.toStrategy(), filteringConfiguration = extensionConfig.filteringConfiguration.toFilteringConfiguration(), strictRunConfiguration = extensionConfig.strictRunConfiguration.toStrictRunConfiguration(), - debug = extensionConfig.debug.get(), ignoreFailures = extensionConfig.ignoreFailures.get(), strictMode = extensionConfig.strictMode.get(), uncompletedTestRetryQuota = extensionConfig.uncompletedTestRetryQuota.orNull, diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonBuildService.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonBuildService.kt index 1848baaa5..4574e6e7e 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonBuildService.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonBuildService.kt @@ -1,6 +1,7 @@ package com.malinskiy.marathon import com.malinskiy.marathon.execution.ComponentInfo +import com.malinskiy.marathon.log.MarathonLogging import com.malinskiy.marathon.worker.WorkerContext import com.malinskiy.marathon.worker.WorkerHandler import org.gradle.api.file.DirectoryProperty @@ -10,8 +11,10 @@ import org.gradle.api.services.BuildServiceParameters abstract class MarathonBuildService : BuildService, WorkerHandler { private val lazyWorkerContext = lazy { + val marathonExtension = parameters.marathonConfig.get() + MarathonLogging.debug = marathonExtension.debug.get() val configuration = createCommonConfiguration( - parameters.marathonConfig.get(), + marathonExtension, parameters.adbPath.get().asFile, parameters.outputDir.get().asFile ) diff --git a/report/execution-timeline/build.gradle.kts b/report/execution-timeline/build.gradle.kts index 1e6deb5f8..6c755eb0d 100644 --- a/report/execution-timeline/build.gradle.kts +++ b/report/execution-timeline/build.gradle.kts @@ -5,6 +5,5 @@ plugins { dependencies { implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlin.logging) implementation(libs.gson) } diff --git a/report/html-report/build.gradle.kts b/report/html-report/build.gradle.kts index 1e6deb5f8..6c755eb0d 100644 --- a/report/html-report/build.gradle.kts +++ b/report/html-report/build.gradle.kts @@ -5,6 +5,5 @@ plugins { dependencies { implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlin.logging) implementation(libs.gson) } diff --git a/vendor/vendor-android/base/build.gradle.kts b/vendor/vendor-android/base/build.gradle.kts index 08e69f22f..dce593010 100644 --- a/vendor/vendor-android/base/build.gradle.kts +++ b/vendor/vendor-android/base/build.gradle.kts @@ -12,8 +12,7 @@ dependencies { implementation(libs.imgscalr) implementation(libs.jackson.annotations) implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlin.logging) - implementation(libs.logback.classic) + implementation(libs.slf4j.api) testImplementation(project(":vendor:vendor-test")) testImplementation(libs.koin.test) diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidLogConfigurator.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidLogConfigurator.kt deleted file mode 100644 index 3696ee8af..000000000 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidLogConfigurator.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.malinskiy.marathon.android - -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.LoggerContext -import ch.qos.logback.classic.PatternLayout -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.ConsoleAppender -import ch.qos.logback.core.encoder.LayoutWrappingEncoder -import com.malinskiy.marathon.log.MarathonLogConfigurator -import org.slf4j.LoggerFactory - -class AndroidLogConfigurator : MarathonLogConfigurator { - override fun configure() { - val loggerContext = LoggerFactory.getILoggerFactory() as? LoggerContext ?: return - - val layout = PatternLayout() - layout.pattern = "%highlight(%.-1level %d{HH:mm:ss.SSS} [%thread] <%logger{40}> %msg%n)" - layout.context = loggerContext - layout.start() - - val encoder = LayoutWrappingEncoder() - encoder.context = loggerContext - encoder.layout = layout - - val consoleAppender = ConsoleAppender() - consoleAppender.isWithJansi = true - consoleAppender.context = loggerContext - consoleAppender.name = "android-custom-console-appender" - consoleAppender.encoder = encoder - consoleAppender.start() - - val rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME) - - // replace the default appenders - rootLogger.detachAndStopAllAppenders() - rootLogger.addAppender(consoleAppender) - } -} diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/di/Modules.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/di/Modules.kt index 5a322b006..6c21f7ae8 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/di/Modules.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/di/Modules.kt @@ -1,7 +1,6 @@ package com.malinskiy.marathon.android.di import com.malinskiy.marathon.android.AndroidComponentCacheKeyProvider -import com.malinskiy.marathon.android.AndroidLogConfigurator import com.malinskiy.marathon.android.AndroidTestParser import com.malinskiy.marathon.android.executor.logcat.LogcatCollector import com.malinskiy.marathon.android.executor.logcat.LogcatListener @@ -9,7 +8,6 @@ import com.malinskiy.marathon.android.executor.logcat.parse.LogcatEventsAdapter import com.malinskiy.marathon.android.executor.logcat.parse.LogcatEventsListener import com.malinskiy.marathon.cache.test.key.ComponentCacheKeyProvider import com.malinskiy.marathon.execution.TestParser -import com.malinskiy.marathon.log.MarathonLogConfigurator import com.malinskiy.marathon.report.logs.LogsProvider import org.koin.dsl.module @@ -20,5 +18,4 @@ val androidModule = module { single { get() } single { get() } single { LogcatEventsAdapter(get()) } - single { AndroidLogConfigurator() } } diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/DebugTestRunListener.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/DebugTestRunListener.kt index a8b6f292a..4728968f4 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/DebugTestRunListener.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/DebugTestRunListener.kt @@ -7,41 +7,41 @@ import com.malinskiy.marathon.test.toSimpleSafeTestName class DebugTestRunListener(private val device: AndroidDevice) : TestRunListener { - private val logger = MarathonLogging.logger("DebugTestRunListener") + private val logger = MarathonLogging.getLogger(DebugTestRunListener::class.java) override fun testRunStarted(runName: String, testCount: Int) { - logger.info { "testRunStarted ${device.serialNumber}" } + logger.info("[{}] testRunStarted", device.serialNumber) } override fun testStarted(test: Test) { - logger.info { "testStarted ${device.serialNumber} test = ${test.toSimpleSafeTestName()}" } + logger.info("[{}] testStarted {}", device.serialNumber, test.toSimpleSafeTestName()) } override fun testAssumptionFailure(test: Test, trace: String) { - logger.info { "testAssumptionFailure ${device.serialNumber} test = ${test.toSimpleSafeTestName()} trace = $trace" } + logger.info("[{}] testAssumptionFailure {} trace = {}", device.serialNumber, test.toSimpleSafeTestName(), trace) } override fun testRunStopped(elapsedTime: Long) { - logger.info { "testRunStopped ${device.serialNumber} elapsedTime = $elapsedTime" } + logger.info("[{}] testRunStopped elapsedTime = {}ms", device.serialNumber, elapsedTime) } override fun testFailed(test: Test, trace: String) { - logger.info { "testFailed ${device.serialNumber} test = ${test.toSimpleSafeTestName()} trace = $trace" } + logger.info("[{}] testFailed {} trace = {}", device.serialNumber, test.toSimpleSafeTestName(), trace) } override fun testEnded(test: Test, testMetrics: Map) { - logger.info { "testEnded ${device.serialNumber} test = ${test.toSimpleSafeTestName()}" } + logger.info("[{}] testEnded {}", device.serialNumber, test.toSimpleSafeTestName()) } override fun testIgnored(test: Test) { - logger.info { "testIgnored ${device.serialNumber} test = ${test.toSimpleSafeTestName()}" } + logger.info("[{}] testIgnored {}", device.serialNumber, test.toSimpleSafeTestName()) } override fun testRunFailed(errorMessage: String) { - logger.info { "testRunFailed ${device.serialNumber} errorMessage = $errorMessage" } + logger.info("[{}] testRunFailed errorMessage = {}", device.serialNumber, errorMessage) } override fun testRunEnded(elapsedTime: Long, runMetrics: Map) { - logger.info { "testRunEnded elapsedTime $elapsedTime" } + logger.info("[{}] testRunEnded elapsedTime = {}ms", device.serialNumber, elapsedTime) } } diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/TestRunResultsListener.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/TestRunResultsListener.kt index 14f50338f..dc47779e0 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/TestRunResultsListener.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/TestRunResultsListener.kt @@ -32,6 +32,7 @@ class TestRunResultsListener( attachmentProviders: List ) : AbstractTestRunResultListener(), AttachmentListener { + private val logger = MarathonLogging.getLogger(TestRunResultsListener::class.java) private val attachments: MutableMap> = mutableMapOf() private val creationTime = timer.currentTimeMillis() @@ -50,8 +51,6 @@ class TestRunResultsListener( attachments[test]!!.add(attachment) } - private val logger = MarathonLogging.logger("TestRunResultsListener") - override fun handleTestRunResults(runResult: TestRunResultsAccumulator) { val results = mergeParameterisedResults(runResult.testResults) val tests = testBatch.tests @@ -91,9 +90,7 @@ class TestRunResultsListener( .createUncompletedTestResults(runResult, device) if (uncompleted.isNotEmpty()) { - uncompleted.forEach { - logger.warn { "uncompleted = ${it.test.toTestName()}, ${device.serialNumber}" } - } + logger.warn("[{}] Uncompleted tests {}", device.serialNumber, uncompleted.joinToString(", ") { it.test.toTestName() }) } deferred.complete(TestBatchResults(testBatch.id, device, testBatch.componentInfo, finished, failed, uncompleted)) @@ -157,8 +154,7 @@ class TestRunResultsListener( private fun Map.Entry.toTestResult(device: Device): TestResult { val testInstanceFromBatch = testBatch.tests.find { it == key } val test = key - val attachments = attachments[test] ?: emptyList() - + val attachments = attachments[test] ?: emptyList() val resultTest = testInstanceFromBatch ?: test return TestResult( diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturer.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturer.kt index 7b69a0cfa..dde988841 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturer.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturer.kt @@ -26,6 +26,8 @@ class ScreenCapturer( val test: Test ) { + private val logger = MarathonLogging.getLogger(ScreenCapturer::class.java) + var attachment: Attachment? = null suspend fun start() = coroutineScope { @@ -93,7 +95,6 @@ class ScreenCapturer( companion object { const val DELAY = 500 const val TIMEOUT_MS = 300L - val logger = MarathonLogging.logger(ScreenCapturer::class.java.simpleName) private const val TARGET_WIDTH = 720 private const val TARGET_HEIGHT = 1280 diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturerTestRunListener.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturerTestRunListener.kt index 68f48ab1c..dd725e8a9 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturerTestRunListener.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/screenshot/ScreenCapturerTestRunListener.kt @@ -22,7 +22,7 @@ class ScreenCapturerTestRunListener( private val attachmentListeners = mutableListOf() private var screenCapturerJob: Job? = null private var screenCapturer: ScreenCapturer? = null - private val logger = MarathonLogging.logger(ScreenCapturerTestRunListener::class.java.simpleName) + private val logger = MarathonLogging.getLogger(ScreenCapturerTestRunListener::class.java) private val dispatcher = Dispatchers.IO.limitedParallelism(1) override fun registerListener(listener: AttachmentListener) { @@ -30,7 +30,7 @@ class ScreenCapturerTestRunListener( } override fun testStarted(test: Test) { - logger.debug { "Starting recording for ${test.toSimpleSafeTestName()}" } + logger.debug("Starting recording for test {}", test.toSimpleSafeTestName()) screenCapturer = ScreenCapturer(device, attachmentManager, test) screenCapturerJob = coroutineScope.async(dispatcher) { screenCapturer?.start() @@ -38,7 +38,7 @@ class ScreenCapturerTestRunListener( } override fun testEnded(test: Test, testMetrics: Map) { - logger.debug { "Finished recording for ${test.toSimpleSafeTestName()}" } + logger.debug("Finished recording for test {}", test.toSimpleSafeTestName()) screenCapturerJob?.cancel() screenCapturer?.attachment?.let { attachment -> diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorder.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorder.kt index d9b2ba49a..8fc34df3a 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorder.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorder.kt @@ -9,12 +9,13 @@ internal class ScreenRecorder( private val device: AndroidDevice, private val remoteFilePath: String ) { + private val logger = MarathonLogging.getLogger(ScreenRecorder::class.java) fun run(handler: ScreenRecorderHandler) { return try { startRecordingTestVideo(handler) } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { - logger.error("Something went wrong while screen recording", e) + logger.error("[{}] Something went wrong while screen recording", device.serialNumber, e) } } @@ -26,11 +27,10 @@ internal class ScreenRecorder( options = options ) } - logger.trace { "Recording finished in ${millis}ms $remoteFilePath" } + logger.trace("[{}] Recording finished in {}ms {}", device.serialNumber, millis, remoteFilePath) } companion object { - private val logger = MarathonLogging.logger("ScreenRecorder") private const val DURATION = 180 private const val BITRATE_MB_PER_SECOND = 1 private val options = ScreenRecorderOptions( diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderStopper.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderStopper.kt index e70a04b72..c570e4522 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderStopper.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderStopper.kt @@ -4,6 +4,8 @@ import com.malinskiy.marathon.android.AndroidDevice import com.malinskiy.marathon.log.MarathonLogging internal class ScreenRecorderStopper(private val device: AndroidDevice) { + private val logger = MarathonLogging.getLogger(ScreenRecorderStopper::class.java) + fun stopScreenRecord() { var hasKilledScreenRecord = true var tries = 0 @@ -33,14 +35,14 @@ internal class ScreenRecorderStopper(private val device: AndroidDevice) { try { val pid = grepPid() if (pid.isNotBlank()) { - logger.trace("Killing PID {} on {}", pid, device.serialNumber) + logger.trace("[{}] Killing PID {}", device.serialNumber, pid) device.safeExecuteShellCommand("kill -2 $pid") return true } else { - logger.trace("Did not kill any screen recording process") + logger.trace("[{}] Did not kill any screen recording process", device.serialNumber) } } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { - logger.error("Error while killing recording processes", e) + logger.error("[{}] Error while killing recording processes", device.serialNumber, e) } return false } @@ -53,7 +55,6 @@ internal class ScreenRecorderStopper(private val device: AndroidDevice) { } companion object { - private val logger = MarathonLogging.logger("ScreenRecorderStopper") private const val SCREEN_RECORD_KILL_ATTEMPTS = 5 /* diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderTestRunListener.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderTestRunListener.kt index 204bd1cc7..52bfb6a18 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderTestRunListener.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/listeners/video/ScreenRecorderTestRunListener.kt @@ -20,12 +20,7 @@ class ScreenRecorderTestRunListener( ) : TestRunListener, AttachmentProvider { private val attachmentListeners = mutableListOf() - - override fun registerListener(listener: AttachmentListener) { - attachmentListeners.add(listener) - } - - private val logger = MarathonLogging.logger("ScreenRecorder") + private val logger = MarathonLogging.getLogger(ScreenRecorderTestRunListener::class.java) private var handler: ScreenRecorderHandler? = null private val screenRecorderStopper = ScreenRecorderStopper(device) @@ -35,6 +30,10 @@ class ScreenRecorderTestRunListener( private val awaitMillis = MS_IN_SECOND + override fun registerListener(listener: AttachmentListener) { + attachmentListeners.add(listener) + } + override fun testStarted(test: Test) { hasFailed = false @@ -61,22 +60,22 @@ class ScreenRecorderTestRunListener( private fun pullVideo(test: Test) { try { - val join = measureTimeMillis { + val joinMillis = measureTimeMillis { recorder?.join(awaitMillis) } - logger.trace { "join ${join}ms" } + logger.trace("[{}] Awaited screen recording in {}ms", device.serialNumber, joinMillis) if (hasFailed) { - val stop = measureTimeMillis { + val stopMillis = measureTimeMillis { screenRecorderStopper.stopScreenRecord() } - logger.trace { "stop ${stop}ms" } + logger.trace("[{}] Stopped screen recording in {}ms", device.serialNumber, stopMillis) pullTestVideo(test) } removeTestVideo(test) } catch (e: InterruptedException) { - logger.warn("Can't stop recording", e) + logger.warn("[{}] Failed to stop screen recording", device.serialNumber, e) } catch (e: TransferException) { - logger.warn("Can't pull video", e) + logger.warn("[{}] Failed to pull video", device.serialNumber, e) } } @@ -90,7 +89,7 @@ class ScreenRecorderTestRunListener( val millis = measureTimeMillis { device.fileManager.pullFile(remoteFilePath, localVideoFile) } - logger.trace { "Pulling finished in ${millis}ms $remoteFilePath " } + logger.trace("[{}] Pulling video finished in {}ms {}", device.serialNumber, millis, remoteFilePath) attachmentListeners.forEach { it.onAttachment(test, attachment) } } @@ -99,6 +98,6 @@ class ScreenRecorderTestRunListener( val millis = measureTimeMillis { device.fileManager.remove(remoteFilePath) } - logger.trace { "Removed file in ${millis}ms $remoteFilePath" } + logger.trace("[{}] Removed file in {}ms {}", device.serialNumber, millis, remoteFilePath) } } diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/logcat/LogcatCollector.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/logcat/LogcatCollector.kt index 103558580..c0cbbf87a 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/logcat/LogcatCollector.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/logcat/LogcatCollector.kt @@ -17,7 +17,7 @@ import java.time.Duration class LogcatCollector : LogcatEventsListener, LogsProvider { - private val logger = MarathonLogging.logger(LogcatCollector::class.java.simpleName) + private val logger = MarathonLogging.getLogger(LogcatCollector::class.java) private val devices: MutableMap = hashMapOf() private val batchCollectors: MutableMap = hashMapOf() @@ -63,7 +63,7 @@ class LogcatCollector : LogcatEventsListener, LogsProvider { is LogcatEvent.BatchFinished -> { val oldState = devices[event.device] if (oldState?.currentBatchId == null) { - logger.error { "Incorrect state: batch ${event.batchId} finished but not started (state = ${oldState})" } + logger.error("[{}] Incorrect state: batch {} finished but not started (state = {})", event.device.serialNumber, event.batchId, oldState) return } batchCollectors[event.batchId]?.onBatchFinished() @@ -73,7 +73,9 @@ class LogcatCollector : LogcatEventsListener, LogsProvider { is LogcatEvent.TestStarted -> { val oldState = devices[event.device] if (oldState?.currentBatchId == null) { - logger.error { "Incorrect state: test ${event.test} started but no active batches found (state = ${oldState})" } + logger.error( + "[{}] Incorrect state: test {} started but no active batches found (state = {})", event.device.serialNumber, event.test, oldState + ) return } @@ -83,12 +85,16 @@ class LogcatCollector : LogcatEventsListener, LogsProvider { is LogcatEvent.TestFinished -> { val oldState = devices[event.device] if (oldState?.currentBatchId == null) { - logger.error { "Incorrect state: test ${event.test} finished but no active batches found (state = ${oldState})" } + logger.error( + "[{}] Incorrect state: test {} finished but no active batches found (state = {})", event.device.serialNumber, event.test, oldState + ) return } if (oldState.currentTest?.test != event.test) { - logger.error { "Incorrect state: test ${event.test} finished but current active test is ${oldState.currentTest}" } + logger.error( + "[{}] Incorrect state: test {} finished but current active test is {}", event.device.serialNumber, event.test, oldState.currentTest + ) return } @@ -115,8 +121,8 @@ class LogcatCollector : LogcatEventsListener, LogsProvider { withTimeout(GET_BATCH_REPORT_TIMEOUT) { batchCollectors[batchId]?.getBatchLogs(forceCreate = false) } - } catch (ignored: TimeoutCancellationException) { - logger.warn { "Timeout reached while waiting for batch $batchId logcat" } + } catch (e: TimeoutCancellationException) { + logger.warn("Timeout reached while waiting for batch {} logcat", batchId, e) null } } diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/model/TestRunResultsAccumulator.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/model/TestRunResultsAccumulator.kt index c58cecff7..5776bb66a 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/model/TestRunResultsAccumulator.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/model/TestRunResultsAccumulator.kt @@ -19,7 +19,7 @@ import java.util.LinkedHashSet */ class TestRunResultsAccumulator : TestRunListener { - val logger = MarathonLogging.logger { } + val logger = MarathonLogging.getLogger(TestRunResultsAccumulator::class.java) var name: String = "not started" private set @@ -107,7 +107,7 @@ class TestRunResultsAccumulator : TestRunListener { private fun updateTestResult(test: Test, status: AndroidTestStatus, trace: String?) { var r: AndroidTestResult? = testResults[test] if (r == null) { - logger.debug { "received test event without test start for ${test.toSimpleSafeTestName()}" } + logger.debug("Received test event without test start for {}", test.toSimpleSafeTestName()) r = AndroidTestResult() } r.status = status diff --git a/vendor/vendor-android/ddmlib/build.gradle.kts b/vendor/vendor-android/ddmlib/build.gradle.kts index bccb88882..7cc7b01ab 100644 --- a/vendor/vendor-android/ddmlib/build.gradle.kts +++ b/vendor/vendor-android/ddmlib/build.gradle.kts @@ -15,8 +15,7 @@ dependencies { implementation(libs.imgscalr) implementation(libs.jackson.annotations) implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlin.logging) - implementation(libs.logback.classic) + implementation(libs.slf4j.api) testImplementation(project(":vendor:vendor-test")) testImplementation(libs.koin.test) diff --git a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/AndroidAppInstaller.kt b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/AndroidAppInstaller.kt index c22a79b78..658847a73 100644 --- a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/AndroidAppInstaller.kt +++ b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/AndroidAppInstaller.kt @@ -15,27 +15,19 @@ class AndroidAppInstaller( configuration: Configuration ) { - companion object { - private const val MAX_RETIRES = 3 - private const val MARSHMALLOW_VERSION_CODE = 23 - private const val MD5_HASH_SIZE = 32 - private const val INSTALLED_TEST_APPS_SCRIPT = "pm list packages -3 | grep -E '\\.test\$' | tr -d '\\r' | cut -d ':' -f 2" - private const val PACKAGE_PREFIX = "package:" - } - - private val logger = MarathonLogging.logger("AndroidAppInstaller") + private val logger = MarathonLogging.getLogger(AndroidAppInstaller::class.java) private val androidConfiguration = configuration.vendorConfiguration as AndroidConfiguration private val installedApps: MutableMap> = hashMapOf() suspend fun ensureInstalled(device: AndroidDevice, componentInfo: AndroidComponentInfo) { val applicationInfo = ApkParser().parseInstrumentationInfo(componentInfo.testApplicationOutput) - logger.debug { "Installing application output to ${device.serialNumber}" } componentInfo.applicationOutput?.let { + logger.debug("[{}] Installing application package {}", device.serialNumber, applicationInfo.applicationPackage) ensureInstalled(device, applicationInfo.applicationPackage, it) } - logger.debug { "Installing instrumentation package to ${device.serialNumber}" } + logger.debug("[{}] Installing instrumentation package {}", device.serialNumber, applicationInfo.instrumentationPackage) ensureInstalled(device, applicationInfo.instrumentationPackage, componentInfo.testApplicationOutput) - logger.debug { "Prepare installation finished for ${device.serialNumber}" } + logger.debug("[{}] Installation finished", device.serialNumber) } fun onDisconnected(device: AndroidDevice) { @@ -52,20 +44,20 @@ class AndroidAppInstaller( track.installationCheck(device.serialNumber, checkStarted, Instant.now()) if (isApkInstalled) { - logger.info("Skipping installation of $appPackage on ${device.serialNumber} - APK is already installed") + logger.info("[{}] Skipping installation of {} - APK is already installed", device.serialNumber, appPackage) } else { cleanupSpaceBeforeInstallation(device) - logger.info("Installing $appPackage, ${appApk.absolutePath} to ${device.serialNumber}") + logger.info("[{}] Installing {} from {}", device.serialNumber, appPackage, appApk.absolutePath) val installationStarted = Instant.now() val installMessage = device.safeInstallPackage(appApk.absolutePath, true, optionalParams(device)) - installMessage?.let { logger.info { it } } + installMessage?.let { logger.info(it) } track.installation(device.serialNumber, installationStarted, Instant.now()) installedApps .getOrPut(device.serialNumber) { hashMapOf() } .put(appPackage, fileHash) } } catch (e: InstallException) { - logger.error(e) { "Error while installing $appPackage, ${appApk.absolutePath} on ${device.serialNumber}" } + logger.error("[{}] Error while installing {} from {}", device.serialNumber, appPackage, appApk.absolutePath, e) throw RuntimeException("Error while installing $appPackage on ${device.serialNumber}", e) } } @@ -77,29 +69,29 @@ class AndroidAppInstaller( .safeExecuteShellCommand("df /storage/emulated -h | grep '/storage/emulated' | awk '{print \$5}'") .substringBefore("%") .toInt() - logger.info { "Used $storageUsedPercentage% of storage on ${device.serialNumber}" } + logger.info("[{}] Used {}% of storage", device.serialNumber, storageUsedPercentage) val usedStorageThresholdInPercents = androidConfiguration.usedStorageThresholdInPercents if (storageUsedPercentage > usedStorageThresholdInPercents) { - logger.warn { "On ${device.serialNumber} used more than $usedStorageThresholdInPercents% of storage" } + logger.warn("[{}] Used more than {}% of storage", device.serialNumber, usedStorageThresholdInPercents) val appsToClean = device.safeExecuteShellCommand(INSTALLED_TEST_APPS_SCRIPT).lines().filter { it.isNotEmpty() } - logger.info { "Removing ${appsToClean.size} apps on ${device.serialNumber}" } + logger.info("[{}] Uninstalling {} apps", device.serialNumber, appsToClean.size) appsToClean.forEach { try { val error = device.safeUninstallPackage(it) if (error != null) { - logger.error { "Error while uninstalling $it on ${device.serialNumber} : $error" } + logger.error("[{}] Error while uninstalling {} : {}", device.serialNumber, it, error) } else { - logger.info { "Uninstalled $it" } + logger.info("[{}] Uninstalled {}", device.serialNumber, it) installedApps[device.serialNumber]?.remove(it) } } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - logger.error(e) { "Error while uninstalling $it on ${device.serialNumber}" } + logger.error("[{}] Error while uninstalling {}", device.serialNumber, it, e) } } } } - private suspend fun isApkInstalled(device: AndroidDevice, appPackage: String, fileHash: String): Boolean { + private fun isApkInstalled(device: AndroidDevice, appPackage: String, fileHash: String): Boolean { if (installedApps[device.serialNumber]?.get(appPackage) == fileHash) { return true } @@ -117,7 +109,7 @@ class AndroidAppInstaller( if (apkPaths.isEmpty()) return null if (apkPaths.size > 1) { - logger.warn { "Multiple packages of $appPackage installed on ${device.serialNumber}, skipping hash check" } + logger.warn("[{}] Multiple packages of {} installed, skipping hash check", device.serialNumber, appPackage) return null } @@ -126,7 +118,7 @@ class AndroidAppInstaller( val hash = md5Output.substringBefore(" ") if (hash.length != MD5_HASH_SIZE) { - logger.warn { "Error while calculating hash for $appPackage on ${device.serialNumber}: ${md5Output}, skipping hash check" } + logger.warn("[{}] Error while calculating hash for {}: {}, skipping hash check", device.serialNumber, appPackage, md5Output) return null } @@ -141,4 +133,12 @@ class AndroidAppInstaller( options += androidConfiguration.installOptions return options.joinToString(" ") } + + companion object { + private const val MAX_RETIRES = 3 + private const val MARSHMALLOW_VERSION_CODE = 23 + private const val MD5_HASH_SIZE = 32 + private const val INSTALLED_TEST_APPS_SCRIPT = "pm list packages -3 | grep -E '\\.test\$' | tr -d '\\r' | cut -d ':' -f 2" + private const val PACKAGE_PREFIX = "package:" + } } diff --git a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/AndroidDeviceTestRunner.kt b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/AndroidDeviceTestRunner.kt index 0127647e1..0a4280aac 100644 --- a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/AndroidDeviceTestRunner.kt +++ b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/AndroidDeviceTestRunner.kt @@ -20,11 +20,10 @@ import java.io.IOException import java.util.concurrent.TimeUnit const val JUNIT_IGNORE_META_PROPERY = "org.junit.Ignore" -const val ERROR_STUCK = "Test got stuck. You can increase the timeout in settings if it's too strict" class AndroidDeviceTestRunner(private val device: DdmlibAndroidDevice) { - private val logger = MarathonLogging.logger("AndroidDeviceTestRunner") + private val logger = MarathonLogging.getLogger(AndroidDeviceTestRunner::class.java) fun execute( configuration: Configuration, @@ -56,23 +55,23 @@ class AndroidDeviceTestRunner(private val device: DdmlibAndroidDevice) { listener.testRunEnded(0, emptyMap()) } } catch (e: ShellCommandUnresponsiveException) { - val errorMessage = "adb unresponsive while running tests ${testBatch.tests.map { it.toTestName() }}" - logger.error(e) { errorMessage } + val errorMessage = "ADB unresponsive while running tests ${testBatch.tests.map { it.toTestName() }}" + logger.error(errorMessage, e) listener.testRunFailed(errorMessage) } catch (e: TimeoutException) { - val errorMessage = "adb timed out while running tests ${testBatch.tests.map { it.toTestName() }}" - logger.error(e) { errorMessage } + val errorMessage = "ADB timed out while running tests ${testBatch.tests.map { it.toTestName() }}" + logger.error(errorMessage, e) listener.testRunFailed(errorMessage) } catch (e: AdbCommandRejectedException) { - val errorMessage = "adb error while running tests ${testBatch.tests.map { it.toTestName() }}" - logger.error(e) { errorMessage } + val errorMessage = "ADB error while running tests ${testBatch.tests.map { it.toTestName() }}" + logger.error(errorMessage, e) listener.testRunFailed(errorMessage) if (e.isDeviceOffline) { throw DeviceLostException(e) } } catch (e: IOException) { - val errorMessage = "adb error while running tests ${testBatch.tests.map { it.toTestName() }}" - logger.error(e) { errorMessage } + val errorMessage = "ADB error while running tests ${testBatch.tests.map { it.toTestName() }}" + logger.error(errorMessage, e) listener.testRunFailed(errorMessage) } } @@ -88,13 +87,13 @@ class AndroidDeviceTestRunner(private val device: DdmlibAndroidDevice) { private fun clearData(androidConfiguration: AndroidConfiguration, info: InstrumentationInfo) { if (androidConfiguration.applicationPmClear) { - device.ddmsDevice.safeClearPackage(info.applicationPackage)?.also { - logger.debug { "Package ${info.applicationPackage} cleared: $it" } + device.ddmsDevice.safeClearPackage(info.applicationPackage)?.let { + logger.debug("[{}] App package {} cleared: {}", device.serialNumber, info.applicationPackage, it) } } if (androidConfiguration.testApplicationPmClear) { - device.ddmsDevice.safeClearPackage(info.instrumentationPackage)?.also { - logger.debug { "Package ${info.applicationPackage} cleared: $it" } + device.ddmsDevice.safeClearPackage(info.instrumentationPackage)?.let { + logger.debug("[{}] Instrumentation package {} cleared: {}", device.serialNumber, info.instrumentationPackage, it) } } } @@ -125,7 +124,7 @@ class AndroidDeviceTestRunner(private val device: DdmlibAndroidDevice) { }.bashEscape() }.toTypedArray() - logger.debug { "tests = ${tests.toList()}" } + logger.debug("[{}] tests = {}", device.serialNumber, tests) runner.setRunName("TestRunName") runner.setMaxTimeToOutputResponse(configuration.testOutputTimeoutMillis * testBatch.tests.size, TimeUnit.MILLISECONDS) diff --git a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibAndroidDevice.kt b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibAndroidDevice.kt index 34a3f4715..a9bd94c86 100644 --- a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibAndroidDevice.kt +++ b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibAndroidDevice.kt @@ -84,7 +84,7 @@ class DdmlibAndroidDevice( private val dispatcher = Dispatchers.IO.limitedParallelism(1) private val job = SupervisorJob(parentJob) private val coroutineScope = CoroutineScope(job + dispatcher) - private val logger = MarathonLogging.logger(DdmlibAndroidDevice::class.java.simpleName) + private val logger = MarathonLogging.getLogger(DdmlibAndroidDevice::class.java) private val logMessagesListener: (List) -> Unit = { it.forEach { msg -> @@ -106,13 +106,13 @@ class DdmlibAndroidDevice( try { ddmsDevice.safeExecuteShellCommand(command, NullOutputReceiver()) } catch (e: TimeoutException) { - logger.error("$errorMessage while executing $command", e) + logger.error("[{}] {} while executing {}", serialNumber, errorMessage, command, e) } catch (e: AdbCommandRejectedException) { - logger.error("$errorMessage while executing $command", e) + logger.error("[{}] {} while executing {}", serialNumber, errorMessage, command, e) } catch (e: ShellCommandUnresponsiveException) { - logger.error("$errorMessage while executing $command", e) + logger.error("[{}] {} while executing {}", serialNumber, errorMessage, command, e) } catch (e: IOException) { - logger.error("$errorMessage while executing $command", e) + logger.error("[{}] {} while executing {}", serialNumber, errorMessage, command, e) } } @@ -264,7 +264,7 @@ class DdmlibAndroidDevice( try { coroutineScope.async { ensureInstalled(androidComponentInfo) }.await() } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - logger.error(e) { "Terminating device $serialNumber due to installation failures" } + logger.error("[{}] Terminating due to installation failures", serialNumber, e) throw DeviceLostException(e) } @@ -284,7 +284,7 @@ class DdmlibAndroidDevice( try { safeExecuteShellCommand("log -t $tag $message") } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - logger.error(e) { "Error during printing logcat message $tag:$message to device $serialNumber" } + logger.error("[{}] Error during printing logcat message {}:{}", serialNumber, tag, message, e) } } @@ -375,7 +375,7 @@ class DdmlibAndroidDevice( try { device.safeExecuteShellCommand("logcat -c", NullOutputReceiver()) } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - logger.warn("Could not clear logcat on device: ${device.serialNumber}", e) + logger.warn("Could not clear logcat on device: {}", device.serialNumber, e) } } diff --git a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibDeviceProvider.kt b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibDeviceProvider.kt index 1ff5b2e64..071200dfb 100644 --- a/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibDeviceProvider.kt +++ b/vendor/vendor-android/ddmlib/src/main/kotlin/com/malinskiy/marathon/android/ddmlib/DdmlibDeviceProvider.kt @@ -45,8 +45,8 @@ class DdmlibDeviceProvider( private val logcatListener: LogcatListener, private val attachmentManager: AttachmentManager ) : DeviceProvider, AndroidDebugBridge.IDeviceChangeListener { - private val logger = MarathonLogging.logger("AndroidDeviceProvider") + private val logger = MarathonLogging.getLogger(DdmlibDeviceProvider::class.java) private val channel: Channel = unboundedChannel() private val devices: ConcurrentMap = ConcurrentHashMap() @@ -62,13 +62,13 @@ class DdmlibDeviceProvider( AndroidDebugBridge.initIfNeeded(false) AndroidDebugBridge.addDeviceChangeListener(this) + logger.debug("Creating ADB bridge") val adb = AndroidDebugBridge.createBridge(vendorConfiguration.adbPath.absolutePath, false, ADB_INIT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) - logger.debug { "Created ADB bridge" } var getDevicesCountdown = config.noDevicesTimeoutMillis val sleepTime = DEFAULT_DDM_LIB_SLEEP_TIME while (!adb.hasInitialDeviceList() || !adb.hasDevices() && getDevicesCountdown >= 0) { - logger.debug { "No devices, waiting..." } + logger.debug("No devices, waiting...") try { delay(sleepTime) @@ -78,34 +78,28 @@ class DdmlibDeviceProvider( getDevicesCountdown -= sleepTime } - logger.debug { "Finished waiting for device list" } + logger.debug("Finished waiting for a device") - adb.devices.forEach { - logger.debug { "Notifying inital connected list: $it" } - deviceConnected(it) + if (adb.devices.isNotEmpty()) { + logger.debug("Initial connected devices: {}", adb.devices.joinToString(", ")) + adb.devices.forEach { + deviceConnected(it) + } } - logger.debug { "Finished notifying" } - - if (!adb.hasInitialDeviceList() || printStackTraceAfterTimeout(PRINT_LOG_TIMEOUT) { !adb.hasDevices() }) { - logger.debug { "Throwing no devices exception in DdmlibDeviceProvider" } - throw NoDevicesException("No devices found.") + if (!adb.hasInitialDeviceList() || printStackTraceAfterTimeout { !adb.hasDevices() }) { + throw NoDevicesException() } - - logger.debug { "Finished DdmlibDeviceProvider initialization" } } - private fun printStackTraceAfterTimeout(timeoutMillis: Long, block: () -> T): T { + private fun printStackTraceAfterTimeout(block: () -> T): T { val currentThread = Thread.currentThread() val isBlockFinished = AtomicBoolean(false) Thread { - Thread.sleep(timeoutMillis) + Thread.sleep(PRINT_LOG_TIMEOUT) if (!isBlockFinished.get() && currentThread.isAlive) { - logger.debug { "Task is not finished within timeout. Printing thread stacktrace:" } - currentThread - .stackTrace - .forEach { logger.debug { it } } + logger.debug("Task is not finished within timeout. Printing thread stacktrace:", currentThread.stackTrace) } }.start() @@ -122,9 +116,8 @@ class DdmlibDeviceProvider( } if (newAndroidDevice != androidDevice) { - logger.debug { "There was a device with the same serial number as the new device ($newAndroidDevice), closing the old device" } + logger.debug("[{}] Device already exists. Closing the old device", newAndroidDevice.serialNumber) androidDevice.close() - logger.debug { "Old device closed ($androidDevice)" } } return newAndroidDevice @@ -156,69 +149,44 @@ class DdmlibDeviceProvider( override fun subscribe() = channel override fun deviceChanged(device: IDevice, changeMask: Int) { - logger.debug { "Device changed: $device" } + logger.debug("Device {} changed, mask = {}", device, changeMask) coroutineScope.launch { - val maybeNewAndroidDevice = DdmlibAndroidDevice( - ddmsDevice = device, - adbPath = vendorConfiguration.adbPath, - track = track, - timer = timer, - androidAppInstaller = androidAppInstaller, - attachmentManager = attachmentManager, - reportsFileManager = fileManager, - serialStrategy = vendorConfiguration.serialStrategy, - logcatListener = logcatListener, - strictRunChecker = strictRunChecker, - parentJob = job - ) - val healthy = maybeNewAndroidDevice.healthy - - logger.debug { "Device ${device.serialNumber} changed state. Healthy = $healthy" } + val newAndroidDevice = device.toDdmlibAndroidDevice() + val healthy = newAndroidDevice.healthy + + logger.debug("[{}] Changed state. Healthy = {}", newAndroidDevice.serialNumber, healthy) if (healthy) { - verifyBooted(maybeNewAndroidDevice) - val androidDevice = getDeviceOrPut(maybeNewAndroidDevice) + verifyBooted(newAndroidDevice) + val androidDevice = getDeviceOrPut(newAndroidDevice) notifyConnected(androidDevice) } else { // This shouldn't have any side effects even if device was previously removed - logger.debug { "Device is not healthy, notifying disconnected $device" } - notifyDisconnected(maybeNewAndroidDevice) - maybeNewAndroidDevice.close() + logger.debug("[{}] Unhealthy. Disconnecting", newAndroidDevice.serialNumber) + notifyDisconnected(newAndroidDevice) + newAndroidDevice.close() } } } override fun deviceConnected(device: IDevice) { - logger.debug { "Device connected: $device" } + logger.debug("Device {} connected", device) coroutineScope.launch { - val maybeNewAndroidDevice = DdmlibAndroidDevice( - ddmsDevice = device, - track = track, - timer = timer, - serialStrategy = vendorConfiguration.serialStrategy, - androidAppInstaller = androidAppInstaller, - attachmentManager = attachmentManager, - reportsFileManager = fileManager, - adbPath = vendorConfiguration.adbPath, - logcatListener = logcatListener, - strictRunChecker = strictRunChecker, - parentJob = job - ) - - val healthy = maybeNewAndroidDevice.healthy - logger.debug("Device ${maybeNewAndroidDevice.serialNumber} connected. Healthy = $healthy") + val newAndroidDevice = device.toDdmlibAndroidDevice() + val healthy = newAndroidDevice.healthy + logger.debug("[{}] Connected. Healthy = {}", newAndroidDevice.serialNumber, healthy) if (healthy) { - verifyBooted(maybeNewAndroidDevice) - val androidDevice = getDeviceOrPut(maybeNewAndroidDevice) + verifyBooted(newAndroidDevice) + val androidDevice = getDeviceOrPut(newAndroidDevice) notifyConnected(androidDevice) } } } override fun deviceDisconnected(device: IDevice) { - logger.debug { "Device ${device.serialNumber} disconnected" } + logger.debug("Device {} disconnected", device) coroutineScope.launch { matchDdmsToDevice(device)?.let { notifyDisconnected(it) @@ -229,7 +197,9 @@ class DdmlibDeviceProvider( } private suspend fun verifyBooted(device: DdmlibAndroidDevice) { - if (!waitForBoot(device)) throw TimeoutException("Timeout waiting for device ${device.serialNumber} to boot") + if (!waitForBoot(device)) { + throw TimeoutException("Timeout waiting for device ${device.serialNumber} to boot") + } } private suspend fun waitForBoot(device: DdmlibAndroidDevice): Boolean { @@ -239,12 +209,12 @@ class DdmlibDeviceProvider( @Suppress("UnusedPrivateProperty") for (i in 1..30) { if (device.booted) { - logger.debug { "Device ${device.serialNumber} booted!" } + logger.debug("[{}] Booted", device.serialNumber) booted = true break } else { delay(1000) - logger.debug { "Device ${device.serialNumber} is still booting..." } + logger.debug("[{}] Still booting...", device.serialNumber) } if (Thread.interrupted() || !currentCoroutineContext().isActive) { @@ -258,8 +228,6 @@ class DdmlibDeviceProvider( } private suspend fun notifyConnected(device: DdmlibAndroidDevice) { - logger.debug { "Notify device connected $device" } - logger.debug { "Send DeviceConnected message for $device" } channel.send(DeviceConnected(device)) } @@ -269,6 +237,21 @@ class DdmlibDeviceProvider( logcatListener.onDeviceDisconnected(device) } + private fun IDevice.toDdmlibAndroidDevice(): DdmlibAndroidDevice = + DdmlibAndroidDevice( + ddmsDevice = this, + adbPath = vendorConfiguration.adbPath, + track = track, + timer = timer, + androidAppInstaller = androidAppInstaller, + attachmentManager = attachmentManager, + reportsFileManager = fileManager, + serialStrategy = vendorConfiguration.serialStrategy, + logcatListener = logcatListener, + strictRunChecker = strictRunChecker, + parentJob = job + ) + private val vendorConfiguration: AndroidConfiguration get() = config.vendorConfiguration as AndroidConfiguration diff --git a/vendor/vendor-android/ddmlib/src/test/kotlin/com/malinskiy/marathon/android/AndroidDeviceTestRunnerSpek.kt b/vendor/vendor-android/ddmlib/src/test/kotlin/com/malinskiy/marathon/android/AndroidDeviceTestRunnerSpek.kt index 14d7e1a00..1bac21772 100644 --- a/vendor/vendor-android/ddmlib/src/test/kotlin/com/malinskiy/marathon/android/AndroidDeviceTestRunnerSpek.kt +++ b/vendor/vendor-android/ddmlib/src/test/kotlin/com/malinskiy/marathon/android/AndroidDeviceTestRunnerSpek.kt @@ -58,7 +58,6 @@ class AndroidDeviceTestRunnerSpek : Spek({ retryStrategy = null, filteringConfiguration = null, strictRunConfiguration = null, - debug = null, ignoreFailures = null, strictMode = null, uncompletedTestRetryQuota = null, diff --git a/vendor/vendor-test/build.gradle.kts b/vendor/vendor-test/build.gradle.kts index b0c587d1b..cb7436f8a 100644 --- a/vendor/vendor-test/build.gradle.kts +++ b/vendor/vendor-test/build.gradle.kts @@ -7,7 +7,6 @@ dependencies { implementation(project(":core")) implementation(libs.jsonassert) implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlin.logging) implementation(libs.kotlin.reflect) implementation(libs.spek.api) } diff --git a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubDevice.kt b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubDevice.kt index 03ab5d155..61854b15f 100644 --- a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubDevice.kt +++ b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubDevice.kt @@ -12,7 +12,6 @@ import com.malinskiy.marathon.execution.TestBatchResults import com.malinskiy.marathon.execution.TestResult import com.malinskiy.marathon.execution.TestStatus import com.malinskiy.marathon.execution.progress.ProgressReporter -import com.malinskiy.marathon.log.MarathonLogging import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.delay @@ -30,8 +29,6 @@ class StubDevice( val crashWithTestBatchException: Boolean = false ) : Device { - private val logger = MarathonLogging.logger(StubDevice::class.java.simpleName) - lateinit var executionResults: Map> var executionIndexMap: MutableMap = mutableMapOf() var timeCounter: Long = 0 @@ -59,17 +56,18 @@ class StubDevice( } deferred.complete( - TestBatchResults(testBatch.id, this, - testBatch.componentInfo, - results.filter { it.status == TestStatus.PASSED }, - results.filter { it.status == TestStatus.FAILURE }, - results.filter { it.status == TestStatus.INCOMPLETE } + TestBatchResults( + batchId = testBatch.id, + device = this, + componentInfo = testBatch.componentInfo, + finished = results.filter { it.status == TestStatus.PASSED }, + failed = results.filter { it.status == TestStatus.FAILURE }, + uncompleted = results.filter { it.status == TestStatus.INCOMPLETE } ) ) } override suspend fun prepare(configuration: Configuration) { - logger.debug { "Preparing" } delay(prepareTimeMillis) } } diff --git a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubMarathonLogConfigurator.kt b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubMarathonLogConfigurator.kt deleted file mode 100644 index ee47ecf5d..000000000 --- a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/StubMarathonLogConfigurator.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.malinskiy.marathon.test - -import com.malinskiy.marathon.log.MarathonLogConfigurator - -class StubMarathonLogConfigurator : MarathonLogConfigurator { - override fun configure() = Unit -} diff --git a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/TestVendorConfiguration.kt b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/TestVendorConfiguration.kt index fafcdf7c9..69825d809 100644 --- a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/TestVendorConfiguration.kt +++ b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/TestVendorConfiguration.kt @@ -4,7 +4,6 @@ import com.malinskiy.marathon.cache.test.key.ComponentCacheKeyProvider import com.malinskiy.marathon.device.DeviceFeature import com.malinskiy.marathon.device.DeviceProvider import com.malinskiy.marathon.execution.TestParser -import com.malinskiy.marathon.log.MarathonLogConfigurator import com.malinskiy.marathon.report.logs.LogsProvider import com.malinskiy.marathon.vendor.VendorConfiguration import org.koin.core.module.Module @@ -19,7 +18,6 @@ class TestVendorConfiguration : VendorConfiguration { single { deviceProvider } single { StubLogsProvider() } single { testParser } - single { StubMarathonLogConfigurator() } } override fun preferableRecorderType(): DeviceFeature? = null diff --git a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/factory/ConfigurationFactory.kt b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/factory/ConfigurationFactory.kt index d36a2bf7e..b09b7899c 100644 --- a/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/factory/ConfigurationFactory.kt +++ b/vendor/vendor-test/src/main/kotlin/com/malinskiy/marathon/test/factory/ConfigurationFactory.kt @@ -68,7 +68,6 @@ class ConfigurationFactory { retryStrategy = retryStrategy, filteringConfiguration = filteringConfiguration, strictRunConfiguration = strictRunConfiguration, - debug = debug, ignoreFailures = ignoreFailures, strictMode = strictMode, uncompletedTestRetryQuota = uncompletedTestRetryQuota,