diff --git a/build.gradle.kts b/build.gradle.kts index 10953bbac..21bd7f45a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,7 @@ allprojects { compilerOptions { jvmTarget.set(JvmTarget.JVM_17) freeCompilerArgs.addAll( + "-Xjvm-default=all", "-opt-in=kotlin.RequiresOptIn" ) } diff --git a/core/src/integrationTest/kotlin/com/malinskiy/marathon/cache/gradle/GradleCacheContainer.kt b/core/src/integrationTest/kotlin/com/malinskiy/marathon/cache/gradle/GradleCacheContainer.kt index ce7f6c362..7d5b13169 100644 --- a/core/src/integrationTest/kotlin/com/malinskiy/marathon/cache/gradle/GradleCacheContainer.kt +++ b/core/src/integrationTest/kotlin/com/malinskiy/marathon/cache/gradle/GradleCacheContainer.kt @@ -2,6 +2,7 @@ package com.malinskiy.marathon.cache.gradle import org.testcontainers.containers.GenericContainer import org.testcontainers.containers.wait.strategy.Wait +import java.net.URI import java.time.Duration class GradleCacheContainer(image: String = "$DEFAULT_IMAGE_NAME:$DEFAULT_TAG") : @@ -14,8 +15,8 @@ class GradleCacheContainer(image: String = "$DEFAULT_IMAGE_NAME:$DEFAULT_TAG") : .withStartupTimeout(Duration.ofSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS)) } - val cacheUrl: String - get() = "http://$containerIpAddress:$httpPort/cache/" + val cacheUrl: URI + get() = URI.create("http://$containerIpAddress:$httpPort/cache/") private val httpPort: Int get() = getMappedPort(DEFAULT_PORT) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt b/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt index 848636b64..615c3238d 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt @@ -51,7 +51,7 @@ class Marathon( private val logger = MarathonLogging.logger("Marathon") private val configurationValidator = LogicalConfigurationValidator() - private val strictRunProcessor = StrictRunProcessor(configuration.strictRunFilterConfiguration) + private val strictRunProcessor = StrictRunProcessor(configuration.strictRunConfiguration) private lateinit var scheduler: Scheduler private lateinit var hook: ShutdownHook diff --git a/core/src/main/kotlin/com/malinskiy/marathon/analytics/TrackerFactory.kt b/core/src/main/kotlin/com/malinskiy/marathon/analytics/TrackerFactory.kt index d7b0a9dac..68437676b 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/analytics/TrackerFactory.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/analytics/TrackerFactory.kt @@ -48,7 +48,7 @@ internal class TrackerFactory( track + mappingTracker track + cacheTestResultsTracker - configuration.customAnalyticsTracker?.let { track + it } + configuration.analyticsTracker?.let { track + it } return delegatingTrackerInternal } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/cache/config/RemoteCacheConfiguration.kt b/core/src/main/kotlin/com/malinskiy/marathon/cache/config/RemoteCacheConfiguration.kt index 26ae31a82..d41d917b2 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/cache/config/RemoteCacheConfiguration.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/cache/config/RemoteCacheConfiguration.kt @@ -1,9 +1,11 @@ package com.malinskiy.marathon.cache.config +import java.net.URI + sealed class RemoteCacheConfiguration { data class Enabled( - val url: String, + val url: URI, val credentials: Credentials? = null ) : RemoteCacheConfiguration() 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 4e0a41bf7..4942d6ea4 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 @@ -22,13 +22,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.ByteArrayOutputStream import java.io.IOException -import java.net.URI import java.net.URL class GradleHttpCacheService(private val configuration: RemoteCacheConfiguration.Enabled) : CacheService { private val httpClient = createClient() - private val baseUri = URI.create(configuration.url) private val logger = MarathonLogging.logger("GradleHttpCacheService") @@ -73,7 +71,7 @@ class GradleHttpCacheService(private val configuration: RemoteCacheConfiguration } private fun CacheKey.entryUrl(): URL = - baseUri.resolve(this.key).toURL() + configuration.url.resolve(key).toURL() private fun createClient(): HttpClient = HttpClient(Apache) { engine { 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 2d58e3889..a51b9fb79 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 @@ -64,7 +64,7 @@ class TestCacheLoader( if (configuration.cache.isEnabled) { val testCacheBlackList: MutableList = arrayListOf() tests.tests.forEach { test -> - if (configuration.strictRunFilterConfiguration.filter.matches(test)) { + if (configuration.strictRunConfiguration.filter.matches(test)) { testCacheBlackList.add(test) } else { testsToCheck.send(TestToCheck(poolId, test, isStrictRun = false)) 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 7c7ede942..e6c107c88 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/Configuration.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/Configuration.kt @@ -22,7 +22,7 @@ private const val DEFAULT_OUTPUT_TIMEOUT_MILLIS: Long = 60_000 data class Configuration( val outputDir: File, - val customAnalyticsTracker: Tracker?, + val cache: CacheConfiguration, val poolingStrategy: PoolingStrategy, val shardingStrategy: ShardingStrategy, val sortingStrategy: SortingStrategy, @@ -30,31 +30,31 @@ data class Configuration( val flakinessStrategy: FlakinessStrategy, val retryStrategy: RetryStrategy, val filteringConfiguration: FilteringConfiguration, - val strictRunFilterConfiguration: StrictRunFilterConfiguration, - val listener: MarathonListener?, + val strictRunConfiguration: StrictRunConfiguration, - val cache: CacheConfiguration, + val debug: Boolean, val ignoreFailures: Boolean, val strictMode: Boolean, val uncompletedTestRetryQuota: Int, - val testClassRegexes: Collection, val includeSerialRegexes: Collection, val excludeSerialRegexes: Collection, + val testClassRegexes: Collection, val ignoreFailureRegexes: Collection, val failFastFailureRegexes: Collection, val testOutputTimeoutMillis: Long, val noDevicesTimeoutMillis: Long, - val debug: Boolean, + val analyticsTracker: Tracker?, + val listener: MarathonListener?, val vendorConfiguration: VendorConfiguration ) { constructor( outputDir: File, - customAnalyticsTracker: Tracker?, + cache: CacheConfiguration?, poolingStrategy: PoolingStrategy?, shardingStrategy: ShardingStrategy?, sortingStrategy: SortingStrategy?, @@ -62,57 +62,58 @@ data class Configuration( flakinessStrategy: FlakinessStrategy?, retryStrategy: RetryStrategy?, filteringConfiguration: FilteringConfiguration?, - strictRunFilterConfiguration: StrictRunFilterConfiguration?, - listener: MarathonListener?, + strictRunConfiguration: StrictRunConfiguration?, - cache: CacheConfiguration?, + debug: Boolean?, ignoreFailures: Boolean?, strictMode: Boolean?, uncompletedTestRetryQuota: Int?, - testClassRegexes: Collection?, includeSerialRegexes: Collection?, excludeSerialRegexes: Collection?, + testClassRegexes: Collection?, ignoreFailureRegexes: Collection?, failFastFailureRegexes: Collection?, testOutputTimeoutMillis: Long?, noDevicesTimeoutMillis: Long?, - debug: Boolean?, + analyticsTracker: Tracker?, + listener: MarathonListener?, vendorConfiguration: VendorConfiguration ) : this( outputDir = outputDir, - customAnalyticsTracker = customAnalyticsTracker, + cache = cache ?: CacheConfiguration(), poolingStrategy = poolingStrategy ?: OmniPoolingStrategy(), shardingStrategy = shardingStrategy ?: ParallelShardingStrategy(), sortingStrategy = sortingStrategy ?: NoSortingStrategy(), batchingStrategy = batchingStrategy ?: IsolateBatchingStrategy(), flakinessStrategy = flakinessStrategy ?: IgnoreFlakinessStrategy(), retryStrategy = retryStrategy ?: NoRetryStrategy(), - filteringConfiguration = filteringConfiguration ?: FilteringConfiguration(emptyList(), emptyList()), - strictRunFilterConfiguration = strictRunFilterConfiguration ?: StrictRunFilterConfiguration(emptyList()), - cache = cache ?: CacheConfiguration(), + filteringConfiguration = filteringConfiguration ?: FilteringConfiguration(), + strictRunConfiguration = strictRunConfiguration ?: StrictRunConfiguration(), + debug = debug ?: true, ignoreFailures = ignoreFailures ?: false, strictMode = strictMode ?: false, - listener = listener, uncompletedTestRetryQuota = uncompletedTestRetryQuota ?: Integer.MAX_VALUE, - testClassRegexes = testClassRegexes ?: listOf(Regex("^((?!Abstract).)*Test$")), includeSerialRegexes = includeSerialRegexes ?: emptyList(), excludeSerialRegexes = excludeSerialRegexes ?: emptyList(), + testClassRegexes = testClassRegexes ?: listOf(Regex("^((?!Abstract).)*Test$")), ignoreFailureRegexes = ignoreFailureRegexes ?: emptyList(), failFastFailureRegexes = failFastFailureRegexes ?: emptyList(), testOutputTimeoutMillis = testOutputTimeoutMillis ?: DEFAULT_OUTPUT_TIMEOUT_MILLIS, noDevicesTimeoutMillis = noDevicesTimeoutMillis ?: DEFAULT_NO_DEVICES_TIMEOUT_MILLIS, - debug = debug ?: true, + analyticsTracker = analyticsTracker, + listener = listener, vendorConfiguration = vendorConfiguration ) fun toMap() = mapOf( "outputDir" to outputDir.absolutePath, + "cache" to cache.toString(), "pooling" to poolingStrategy.toString(), "sharding" to shardingStrategy.toString(), "sorting" to sortingStrategy.toString(), @@ -120,16 +121,15 @@ data class Configuration( "flakiness" to flakinessStrategy.toString(), "retry" to retryStrategy.toString(), "filtering" to filteringConfiguration.toString(), - "strictRunFilter" to strictRunFilterConfiguration.toString(), - "cache" to cache.toString(), + "strictRun" to strictRunConfiguration.toString(), + "debug" to debug.toString(), "ignoreFailures" to ignoreFailures.toString(), "strictMode" to strictMode.toString(), - "testClassRegexes" to testClassRegexes.toString(), "includeSerialRegexes" to includeSerialRegexes.toString(), "excludeSerialRegexes" to excludeSerialRegexes.toString(), + "testClassRegexes" to testClassRegexes.toString(), "testOutputTimeoutMillis" to testOutputTimeoutMillis.toString(), "noDevicesTimeoutMillis" to noDevicesTimeoutMillis.toString(), - "debug" to debug.toString(), "vendorConfiguration" to vendorConfiguration.toString() ) } diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunChecker.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunChecker.kt index 0789a4802..65ab181df 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunChecker.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunChecker.kt @@ -10,7 +10,7 @@ interface StrictRunChecker { class ConfigurationStrictRunChecker(private val configuration: Configuration) : StrictRunChecker { override fun isStrictRun(test: Test): Boolean = - configuration.strictMode || configuration.strictRunFilterConfiguration.filter.matches(test) + configuration.strictMode || configuration.strictRunConfiguration.filter.matches(test) override fun hasFailFastFailures(stackTrace: String?): Boolean = stackTrace?.let { configuration.failFastFailureRegexes.any { it.matches(stackTrace) } } == true diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunFilterConfiguration.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunConfiguration.kt similarity index 86% rename from core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunFilterConfiguration.kt rename to core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunConfiguration.kt index 0e83d7ce2..190e5a897 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunFilterConfiguration.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunConfiguration.kt @@ -2,7 +2,7 @@ package com.malinskiy.marathon.execution import com.fasterxml.jackson.annotation.JsonProperty -data class StrictRunFilterConfiguration( +data class StrictRunConfiguration( @JsonProperty("filter", required = false) val filter: Collection = emptyList(), @JsonProperty("runs", required = false) val runs: Int = 1 ) diff --git a/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunProcessor.kt b/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunProcessor.kt index 0d5cb7ad1..e4f790e3b 100644 --- a/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunProcessor.kt +++ b/core/src/main/kotlin/com/malinskiy/marathon/execution/StrictRunProcessor.kt @@ -2,7 +2,7 @@ package com.malinskiy.marathon.execution import com.malinskiy.marathon.test.Test -class StrictRunProcessor(private val configuration: StrictRunFilterConfiguration) { +class StrictRunProcessor(private val configuration: StrictRunConfiguration) { fun processShard(shard: TestShard): TestShard { var testsForStrictRun = if (configuration.filter.isEmpty()) emptyList() else shard.tests.toList() 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 50e5210a3..df2f8cbdc 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 @@ -107,7 +107,7 @@ class TestResultReporter( } private fun Test.isStrictRun(): Boolean = - configuration.strictMode || configuration.strictRunFilterConfiguration.filter.matches(this) + configuration.strictMode || configuration.strictRunConfiguration.filter.matches(this) fun addShard(shard: TestShard) { val allTests = shard.tests + shard.flakyTests diff --git a/core/src/test/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporterSpec.kt b/core/src/test/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporterSpec.kt index cd3b9255c..a72fabcd5 100644 --- a/core/src/test/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporterSpec.kt +++ b/core/src/test/kotlin/com/malinskiy/marathon/execution/queue/TestResultReporterSpec.kt @@ -5,7 +5,7 @@ import com.malinskiy.marathon.analytics.internal.pub.Track import com.malinskiy.marathon.createDeviceInfo import com.malinskiy.marathon.device.DevicePoolId import com.malinskiy.marathon.execution.SimpleClassnameFilter -import com.malinskiy.marathon.execution.StrictRunFilterConfiguration +import com.malinskiy.marathon.execution.StrictRunConfiguration import com.malinskiy.marathon.execution.TestFilter import com.malinskiy.marathon.execution.TestResult import com.malinskiy.marathon.execution.TestShard @@ -44,7 +44,7 @@ object TestResultReporterSpec : Spek( fun strictFilterReporter(filter: TestFilter) = TestResultReporter( poolId, - defaultConfig.copy(strictRunFilterConfiguration = StrictRunFilterConfiguration(filter = listOf(filter), runs = 3)), + defaultConfig.copy(strictRunConfiguration = StrictRunConfiguration(filter = listOf(filter), runs = 3)), track ).apply { addShard(TestShard(listOf(test, test, test))) diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/BatchingStrategyConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/BatchingStrategyConfiguration.kt index e949b2e2b..5dbbea076 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/BatchingStrategyConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/BatchingStrategyConfiguration.kt @@ -3,31 +3,43 @@ package com.malinskiy.marathon import com.malinskiy.marathon.execution.strategy.BatchingStrategy import com.malinskiy.marathon.execution.strategy.impl.batching.FixedSizeBatchingStrategy import com.malinskiy.marathon.execution.strategy.impl.batching.IsolateBatchingStrategy -import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested +import java.time.Duration import java.time.Instant -class BatchingStrategyConfiguration { - var fixedSize: FixedSizeBatchingStrategyConfiguration? = null +interface BatchingStrategyConfiguration { + @get:Nested + val fixedSize: FixedSizeBatchingStrategyConfiguration - fun fixedSize(block: FixedSizeBatchingStrategyConfiguration.() -> Unit) { - fixedSize = FixedSizeBatchingStrategyConfiguration().also(block) + fun fixedSize(action: Action) { + fixedSize.initDefaults() + action.execute(fixedSize) } +} + +interface FixedSizeBatchingStrategyConfiguration { + val size: Property + val durationMillis: Property + val percentile: Property + val timeLimit: Property + val lastMileLength: Property - fun fixedSize(closure: Closure<*>) { - fixedSize = FixedSizeBatchingStrategyConfiguration() - closure.delegate = fixedSize - closure.call() + fun initDefaults() { + size.convention(1) + lastMileLength.convention(0) } } -class FixedSizeBatchingStrategyConfiguration { - var size = 1 - var durationMillis: Long? = null - var percentile: Double? = null - var timeLimit: Instant? = null - var lastMileLength: Int = 0 -} +internal fun BatchingStrategyConfiguration.toStrategy(): BatchingStrategy = + if (fixedSize.size.isPresent) fixedSize.toStrategy() else IsolateBatchingStrategy() -fun BatchingStrategyConfiguration.toStrategy(): BatchingStrategy = fixedSize?.let { - FixedSizeBatchingStrategy(it.size, it.durationMillis, it.percentile, it.timeLimit, it.lastMileLength) -} ?: IsolateBatchingStrategy() +private fun FixedSizeBatchingStrategyConfiguration.toStrategy(): FixedSizeBatchingStrategy = + FixedSizeBatchingStrategy( + size = size.get(), + durationMillis = durationMillis.orNull, + percentile = percentile.orNull, + timeLimit = timeLimit.orNull?.let { Instant.now().minus(it) }, + lastMileLength = lastMileLength.get() + ) diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/CachePluginConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/CachePluginConfiguration.kt index 1898c6dab..fea4dfe47 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/CachePluginConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/CachePluginConfiguration.kt @@ -4,66 +4,66 @@ import com.malinskiy.marathon.cache.config.Credentials import com.malinskiy.marathon.cache.config.LocalCacheConfiguration import com.malinskiy.marathon.cache.config.RemoteCacheConfiguration import com.malinskiy.marathon.execution.CacheConfiguration -import groovy.lang.Closure -import java.io.File +import org.gradle.api.Action +import org.gradle.api.credentials.PasswordCredentials +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested +import java.net.URI -open class CachePluginConfiguration { +interface CachePluginConfiguration { + @get:Nested + val local: LocalCacheExtension - var localExtension: LocalCacheExtension? = null - var remoteExtension: RemoteCacheExtension? = null + @get:Nested + val remote: RemoteCacheExtension - fun local(closure: Closure<*>) { - localExtension = LocalCacheExtension() - closure.delegate = localExtension - closure.call() + fun local(action: Action) { + local.initDefaults() + action.execute(local) } - fun remote(closure: Closure<*>) { - remoteExtension = RemoteCacheExtension() - closure.delegate = remoteExtension - closure.call() + fun remote(action: Action) { + action.execute(remote) } +} - fun local(block: LocalCacheExtension.() -> Unit) { - val config = localExtension ?: LocalCacheExtension() - config.also(block) - localExtension = config - } +interface LocalCacheExtension { + val directory: DirectoryProperty + val removeUnusedEntriesAfterDays: Property - fun remote(block: RemoteCacheExtension.() -> Unit) { - val config = remoteExtension ?: RemoteCacheExtension() - config.also(block) - remoteExtension = config + fun initDefaults() { + removeUnusedEntriesAfterDays.convention(7) } } -private val DEFAULT_LOCAL_CACHE_DIRECTORY = File("~/cache/marathon") -private const val DEFAULT_LOCAL_UNUSED_ENTRIES_DELETE_AFTER_DAYS = 7 - -open class LocalCacheExtension { - var directory: File = DEFAULT_LOCAL_CACHE_DIRECTORY - var removeUnusedEntriesAfterDays: Int = DEFAULT_LOCAL_UNUSED_ENTRIES_DELETE_AFTER_DAYS +interface RemoteCacheExtension { + val url: Property + val credentials: Property } -private fun LocalCacheExtension?.toConfig(): LocalCacheConfiguration = - this?.let { - LocalCacheConfiguration.Enabled(it.directory, it.removeUnusedEntriesAfterDays) - } ?: LocalCacheConfiguration.Disabled +internal fun CachePluginConfiguration.toCacheConfiguration(): CacheConfiguration = + CacheConfiguration( + local = local.toConfig(), + remote = remote.toConfig() + ) -open class RemoteCacheExtension { - var url: String? = null - var credentials: Credentials? = null -} +private fun LocalCacheExtension.toConfig(): LocalCacheConfiguration = + if (directory.isPresent) { + LocalCacheConfiguration.Enabled(directory.get().asFile, removeUnusedEntriesAfterDays.get()) + } else { + LocalCacheConfiguration.Disabled + } -private fun RemoteCacheExtension?.toConfig(): RemoteCacheConfiguration = - this?.let { - val url = it.url ?: throw IllegalArgumentException("Remote cache URL is required for remote cache configuration") - RemoteCacheConfiguration.Enabled(url, it.credentials) - } ?: RemoteCacheConfiguration.Disabled +private fun RemoteCacheExtension.toConfig(): RemoteCacheConfiguration = + if (url.isPresent) { + RemoteCacheConfiguration.Enabled(url.get(), credentials.orNull?.toCredentials()) + } else { + RemoteCacheConfiguration.Disabled + } -fun CachePluginConfiguration.toCacheConfiguration(): CacheConfiguration { - return CacheConfiguration( - local = localExtension.toConfig(), - remote = remoteExtension.toConfig() +private fun PasswordCredentials.toCredentials(): Credentials = + Credentials( + userName = requireNotNull(username) { "Username is required" }, + password = requireNotNull(password) { "Password is required" } ) -} 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 d3217343c..dfff9295a 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 @@ -1,10 +1,6 @@ package com.malinskiy.marathon import com.malinskiy.marathon.android.AndroidConfiguration -import com.malinskiy.marathon.android.DEFAULT_APPLICATION_PM_CLEAR -import com.malinskiy.marathon.android.DEFAULT_AUTO_GRANT_PERMISSION -import com.malinskiy.marathon.android.DEFAULT_INSTALL_OPTIONS -import com.malinskiy.marathon.android.DEFAULT_USED_STORAGE_THRESHOLD_PERCENTS import com.malinskiy.marathon.android.serial.SerialStrategy import com.malinskiy.marathon.execution.Configuration import ddmlibModule @@ -16,61 +12,49 @@ internal fun createCommonConfiguration( outputDir: File ): Configuration = Configuration( outputDir = outputDir, - customAnalyticsTracker = extensionConfig.customAnalyticsTracker, - poolingStrategy = extensionConfig.poolingStrategy?.toStrategy(), - shardingStrategy = extensionConfig.shardingStrategy?.toStrategy(), - sortingStrategy = extensionConfig.sortingStrategy?.toStrategy(), - batchingStrategy = extensionConfig.batchingStrategy?.toStrategy(), - flakinessStrategy = extensionConfig.flakinessStrategy?.toStrategy(), - retryStrategy = extensionConfig.retryStrategy?.toStrategy(), - filteringConfiguration = extensionConfig.filteringConfiguration?.toFilteringConfiguration(), - strictRunFilterConfiguration = extensionConfig.strictRunFilterConfiguration?.toStrictRunFilterConfiguration(), - cache = extensionConfig.cache?.toCacheConfiguration(), - ignoreFailures = extensionConfig.ignoreFailures, - strictMode = extensionConfig.strictMode, - listener = extensionConfig.listener, - uncompletedTestRetryQuota = extensionConfig.uncompletedTestRetryQuota, - testClassRegexes = extensionConfig.testClassRegexes?.map { it.toRegex() }, - includeSerialRegexes = extensionConfig.includeSerialRegexes?.map { it.toRegex() }, - excludeSerialRegexes = extensionConfig.excludeSerialRegexes?.map { it.toRegex() }, - ignoreFailureRegexes = extensionConfig.ignoreFailureRegexes?.map { it.toRegex(RegexOption.DOT_MATCHES_ALL) }, - failFastFailureRegexes = extensionConfig.failFastFailureRegexes?.map { it.toRegex(RegexOption.DOT_MATCHES_ALL) }, - testOutputTimeoutMillis = extensionConfig.testOutputTimeoutMillis, - noDevicesTimeoutMillis = extensionConfig.noDevicesTimeoutMillis, - debug = extensionConfig.debug, + cache = extensionConfig.cache.toCacheConfiguration(), + poolingStrategy = extensionConfig.poolingStrategy.toStrategy(), + shardingStrategy = extensionConfig.shardingStrategy.toStrategy(), + sortingStrategy = extensionConfig.sortingStrategy.toStrategy(), + batchingStrategy = extensionConfig.batchingStrategy.toStrategy(), + flakinessStrategy = extensionConfig.flakinessStrategy.toStrategy(), + 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, + includeSerialRegexes = extensionConfig.includeSerialRegexes.get().map { it.toRegex() }, + excludeSerialRegexes = extensionConfig.excludeSerialRegexes.get().map { it.toRegex() }, + testClassRegexes = extensionConfig.testClassRegexes.get().map { it.toRegex() }, + ignoreFailureRegexes = extensionConfig.ignoreFailureRegexes.get().map { it.toRegex(RegexOption.DOT_MATCHES_ALL) }, + failFastFailureRegexes = extensionConfig.failFastFailureRegexes.get().map { it.toRegex(RegexOption.DOT_MATCHES_ALL) }, + testOutputTimeoutMillis = extensionConfig.testOutputTimeoutMillis.orNull, + noDevicesTimeoutMillis = extensionConfig.noDevicesTimeoutMillis.orNull, + analyticsTracker = extensionConfig.analyticsTracker.orNull, + listener = extensionConfig.listener.orNull, vendorConfiguration = createAndroidConfiguration(extensionConfig, adbPath) ) private fun createAndroidConfiguration(extension: MarathonExtension, adbPath: File): AndroidConfiguration { - val autoGrantPermission = extension.autoGrantPermission ?: DEFAULT_AUTO_GRANT_PERMISSION - val instrumentationArgs = extension.instrumentationArgs - val applicationPmClear = extension.applicationPmClear ?: DEFAULT_APPLICATION_PM_CLEAR - val testApplicationPmClear = extension.testApplicationPmClear ?: DEFAULT_APPLICATION_PM_CLEAR - val installOptions = extension.installOptions ?: DEFAULT_INSTALL_OPTIONS - val preferableRecorderType = extension.preferableRecorderType - val serialStrategy = extension.serialStrategy - ?.let { - when (it) { - SerialStrategyConfiguration.AUTOMATIC -> SerialStrategy.AUTOMATIC - SerialStrategyConfiguration.MARATHON_PROPERTY -> SerialStrategy.MARATHON_PROPERTY - SerialStrategyConfiguration.BOOT_PROPERTY -> SerialStrategy.BOOT_PROPERTY - SerialStrategyConfiguration.HOSTNAME -> SerialStrategy.HOSTNAME - SerialStrategyConfiguration.DDMS -> SerialStrategy.DDMS - } - } - ?: SerialStrategy.AUTOMATIC - val usedStorageThresholdInPercents = extension.usedStorageThresholdInPercents ?: DEFAULT_USED_STORAGE_THRESHOLD_PERCENTS - + val serialStrategy = when (extension.serialStrategy.get()) { + SerialStrategyConfiguration.AUTOMATIC -> SerialStrategy.AUTOMATIC + SerialStrategyConfiguration.MARATHON_PROPERTY -> SerialStrategy.MARATHON_PROPERTY + SerialStrategyConfiguration.BOOT_PROPERTY -> SerialStrategy.BOOT_PROPERTY + SerialStrategyConfiguration.HOSTNAME -> SerialStrategy.HOSTNAME + SerialStrategyConfiguration.DDMS -> SerialStrategy.DDMS + } return AndroidConfiguration( - adbPath, - listOf(ddmlibModule), - autoGrantPermission, - instrumentationArgs, - applicationPmClear, - testApplicationPmClear, - installOptions, - preferableRecorderType, - serialStrategy, - usedStorageThresholdInPercents + adbPath = adbPath, + implementationModules = listOf(ddmlibModule), + autoGrantPermission = extension.autoGrantPermission.get(), + instrumentationArgs = extension.instrumentationArgs.get(), + applicationPmClear = extension.applicationPmClear.get(), + testApplicationPmClear = extension.testApplicationPmClear.get(), + installOptions = extension.installOptions.get(), + preferableRecorderType = extension.preferableRecorderType.orNull, + serialStrategy = serialStrategy, + usedStorageThresholdInPercents = extension.usedStorageThresholdInPercents.get() ) } diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilterWrapper.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilterWrapper.kt index 8bab1263f..6a90adddd 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilterWrapper.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilterWrapper.kt @@ -5,27 +5,17 @@ import com.malinskiy.marathon.execution.FullyQualifiedClassnameFilter import com.malinskiy.marathon.execution.SimpleClassnameFilter import com.malinskiy.marathon.execution.TestFilter import com.malinskiy.marathon.execution.TestPackageFilter +import org.gradle.api.provider.ListProperty -open class FilterWrapper { - open var simpleClassNameFilter: ArrayList? = null - open var fullyQualifiedClassnameFilter: ArrayList? = null - open var testPackageFilter: ArrayList? = null - open var annotationFilter: ArrayList? = null +interface FilterWrapper { + val simpleClassNameFilter: ListProperty + val fullyQualifiedClassnameFilter: ListProperty + val testPackageFilter: ListProperty + val annotationFilter: ListProperty } -fun FilterWrapper.toList(): List { - val mutableList = mutableListOf() - this.annotationFilter?.map { AnnotationFilter(it.toRegex()) }?.let { - mutableList.addAll(it) - } - this.fullyQualifiedClassnameFilter?.map { FullyQualifiedClassnameFilter(it.toRegex()) }?.let { - mutableList.addAll(it) - } - this.testPackageFilter?.map { TestPackageFilter(it.toRegex()) }?.let { - mutableList.addAll(it) - } - this.simpleClassNameFilter?.map { SimpleClassnameFilter(it.toRegex()) }?.let { - mutableList.addAll(it) - } - return mutableList -} +internal fun FilterWrapper.toList(): List = + annotationFilter.get().map { AnnotationFilter(it.toRegex()) } + + fullyQualifiedClassnameFilter.get().map { FullyQualifiedClassnameFilter(it.toRegex()) } + + testPackageFilter.get().map { TestPackageFilter(it.toRegex()) } + + simpleClassNameFilter.get().map { SimpleClassnameFilter(it.toRegex()) } diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilteringPluginConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilteringPluginConfiguration.kt index 3a5646d01..57b397927 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilteringPluginConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FilteringPluginConfiguration.kt @@ -1,44 +1,27 @@ package com.malinskiy.marathon import com.malinskiy.marathon.execution.FilteringConfiguration -import com.malinskiy.marathon.execution.TestFilter -import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.tasks.Nested -open class FilteringPluginConfiguration { - //groovy - var groovyWhiteList: FilterWrapper? = null - var groovyBlackList: FilterWrapper? = null +interface FilteringPluginConfiguration { + @get:Nested + val whitelist: FilterWrapper - fun whitelist(closure: Closure<*>) { - groovyWhiteList = FilterWrapper() - closure.delegate = groovyWhiteList - closure.call() - } - - fun blacklist(closure: Closure<*>) { - groovyBlackList = FilterWrapper() - closure.delegate = groovyBlackList - closure.call() - } + @get:Nested + val blacklist: FilterWrapper - //kts - var whitelist: MutableCollection = mutableListOf() - var blacklist: MutableCollection = mutableListOf() - fun whitelist(block: MutableCollection.() -> Unit) { - whitelist.also(block) + fun whitelist(action: Action) { + action.execute(whitelist) } - fun blacklist(block: MutableCollection.() -> Unit) { - blacklist.also(block) + fun blacklist(action: Action) { + action.execute(blacklist) } } -fun FilteringPluginConfiguration.toFilteringConfiguration(): FilteringConfiguration { - if (groovyWhiteList != null || groovyBlackList != null) { - val white = groovyWhiteList?.toList() ?: emptyList() - - val black = groovyBlackList?.toList() ?: emptyList() - return FilteringConfiguration(white, black) - } - return FilteringConfiguration(whitelist, blacklist) -} +internal fun FilteringPluginConfiguration.toFilteringConfiguration(): FilteringConfiguration = + FilteringConfiguration( + whitelist = whitelist.toList(), + blacklist = blacklist.toList() + ) diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FlakinessStrategyConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FlakinessStrategyConfiguration.kt index cdc45cd79..95b7a006b 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FlakinessStrategyConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/FlakinessStrategyConfiguration.kt @@ -3,33 +3,44 @@ package com.malinskiy.marathon import com.malinskiy.marathon.execution.strategy.FlakinessStrategy import com.malinskiy.marathon.execution.strategy.impl.flakiness.IgnoreFlakinessStrategy import com.malinskiy.marathon.execution.strategy.impl.flakiness.ProbabilityBasedFlakinessStrategy -import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested +import java.time.Duration import java.time.Instant -import java.time.temporal.ChronoUnit -class FlakinessStrategyConfiguration { - var probabilityBased: ProbabilityBasedFlakinessStrategyConfiguration? = null +interface FlakinessStrategyConfiguration { + @get:Nested + val probabilityBased: ProbabilityBasedFlakinessStrategyConfiguration - fun probabilityBased(block: ProbabilityBasedFlakinessStrategyConfiguration.() -> Unit) { - probabilityBased = ProbabilityBasedFlakinessStrategyConfiguration().also(block) + fun probabilityBased(action: Action) { + probabilityBased.initDefaults() + action.execute(probabilityBased) } +} + +interface ProbabilityBasedFlakinessStrategyConfiguration { + val minSuccessRate: Property + val maxCount: Property + val timeLimit: Property - fun probabilityBased(closure: Closure<*>) { - probabilityBased = ProbabilityBasedFlakinessStrategyConfiguration() - closure.delegate = probabilityBased - closure.call() + fun initDefaults() { + minSuccessRate.convention(DEFAULT_MIN_SUCCESS_RATE) + maxCount.convention(DEFAULT_MAX_FLAKY_TESTS_COUNT) + timeLimit.convention(Duration.ofDays(DEFAULT_DAYS_COUNT)) } } -private const val DEFAULT_MIN_SUCCESS_RATE = 0.8 -private const val DEFAULT_MAX_FLAKY_TESTS_COUNT = 3 +internal fun FlakinessStrategyConfiguration.toStrategy(): FlakinessStrategy = + if (probabilityBased.minSuccessRate.isPresent) probabilityBased.toStrategy() else IgnoreFlakinessStrategy() -class ProbabilityBasedFlakinessStrategyConfiguration { - var minSuccessRate: Double = DEFAULT_MIN_SUCCESS_RATE - var maxCount: Int = DEFAULT_MAX_FLAKY_TESTS_COUNT - var timeLimit: Instant = Instant.now().minus(DEFAULT_DAYS_COUNT, ChronoUnit.DAYS) -} +private fun ProbabilityBasedFlakinessStrategyConfiguration.toStrategy(): ProbabilityBasedFlakinessStrategy = + ProbabilityBasedFlakinessStrategy( + minSuccessRate = minSuccessRate.get(), + maxCount = maxCount.get(), + timeLimit = Instant.now().minus(timeLimit.get()) + ) -fun FlakinessStrategyConfiguration.toStrategy(): FlakinessStrategy = probabilityBased?.let { - ProbabilityBasedFlakinessStrategy(it.minSuccessRate, it.maxCount, it.timeLimit) -} ?: IgnoreFlakinessStrategy() +private const val DEFAULT_MIN_SUCCESS_RATE = 0.8 +private const val DEFAULT_MAX_FLAKY_TESTS_COUNT = 3 +private const val DEFAULT_DAYS_COUNT = 30L diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonExtension.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonExtension.kt index 6accc42bc..a4a4c1e9d 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonExtension.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonExtension.kt @@ -1,155 +1,133 @@ package com.malinskiy.marathon import com.malinskiy.marathon.analytics.internal.pub.Tracker +import com.malinskiy.marathon.android.DEFAULT_APPLICATION_PM_CLEAR +import com.malinskiy.marathon.android.DEFAULT_AUTO_GRANT_PERMISSION +import com.malinskiy.marathon.android.DEFAULT_TEST_APPLICATION_PM_CLEAR +import com.malinskiy.marathon.android.DEFAULT_USED_STORAGE_THRESHOLD_PERCENTS import com.malinskiy.marathon.device.DeviceFeature import com.malinskiy.marathon.execution.MarathonListener -import groovy.lang.Closure - -open class MarathonExtension { - var customAnalyticsTracker: Tracker? = null - - var poolingStrategy: PoolingStrategyConfiguration? = null - var shardingStrategy: ShardingStrategyConfiguration? = null - var sortingStrategy: SortingStrategyConfiguration? = null - var batchingStrategy: BatchingStrategyConfiguration? = null - var flakinessStrategy: FlakinessStrategyConfiguration? = null - var retryStrategy: RetryStrategyConfiguration? = null - var filteringConfiguration: FilteringPluginConfiguration? = null - var strictRunFilterConfiguration: StrictRunFilterPluginConfiguration? = null - var listener: MarathonListener? = null - - var cache: CachePluginConfiguration? = null - var ignoreFailures: Boolean? = null - var strictMode: Boolean? = null - var uncompletedTestRetryQuota: Int? = null - - var testClassRegexes: Collection? = null - var includeSerialRegexes: Collection? = null - var excludeSerialRegexes: Collection? = null - var ignoreFailureRegexes: Collection? = null +import org.gradle.api.Action +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested - /** - * Tests that have failed with stack traces that match that property wouldn't be rerun - * It applies to both failed and uncompleted tests - * This has higher priority than uncompletedRetriesQuota or amount of runs in StrictRunFilterPluginConfiguration - */ - var failFastFailureRegexes: Collection? = null +interface MarathonExtension { + @get:Nested + val cache: CachePluginConfiguration - var testOutputTimeoutMillis: Long? = null - var noDevicesTimeoutMillis: Long? = null - var debug: Boolean? = null + @get:Nested + val poolingStrategy: PoolingStrategyConfiguration - var applicationPmClear: Boolean? = null - var testApplicationPmClear: Boolean? = null - var installOptions: String? = null - var serialStrategy: SerialStrategyConfiguration? = null + @get:Nested + val shardingStrategy: ShardingStrategyConfiguration - var preferableRecorderType: DeviceFeature? = null + @get:Nested + val sortingStrategy: SortingStrategyConfiguration - //Android specific for now - var autoGrantPermission: Boolean? = null - var instrumentationArgs: MutableMap = mutableMapOf() - var usedStorageThresholdInPercents: Int? = null + @get:Nested + val batchingStrategy: BatchingStrategyConfiguration - //Kotlin way - fun cache(block: CachePluginConfiguration.() -> Unit) { - cache = CachePluginConfiguration().also(block) - } + @get:Nested + val flakinessStrategy: FlakinessStrategyConfiguration - fun batchingStrategy(block: BatchingStrategyConfiguration.() -> Unit) { - batchingStrategy = BatchingStrategyConfiguration().also(block) - } + @get:Nested + val retryStrategy: RetryStrategyConfiguration - fun flakinessStrategy(block: FlakinessStrategyConfiguration.() -> Unit) { - flakinessStrategy = FlakinessStrategyConfiguration().also(block) - } + @get:Nested + val filteringConfiguration: FilteringPluginConfiguration - fun poolingStrategy(block: PoolingStrategyConfiguration.() -> Unit) { - poolingStrategy = PoolingStrategyConfiguration().also(block) - } + @get:Nested + val strictRunConfiguration: StrictRunPluginConfiguration - fun retryStrategy(block: RetryStrategyConfiguration.() -> Unit) { - retryStrategy = RetryStrategyConfiguration().also(block) - } + val serialStrategy: Property + val preferableRecorderType: Property - fun shardingStrategy(block: ShardingStrategyConfiguration.() -> Unit) { - shardingStrategy = ShardingStrategyConfiguration().also(block) - } + val autoGrantPermission: Property + val debug: Property + val ignoreFailures: Property + val strictMode: Property - fun sortingStrategy(block: SortingStrategyConfiguration.() -> Unit) { - sortingStrategy = SortingStrategyConfiguration().also(block) - } + val applicationPmClear: Property + val testApplicationPmClear: Property - fun filteringConfiguration(block: FilteringPluginConfiguration.() -> Unit) { - filteringConfiguration = FilteringPluginConfiguration().also(block) - } + val includeSerialRegexes: ListProperty + val excludeSerialRegexes: ListProperty + val testClassRegexes: ListProperty + val ignoreFailureRegexes: ListProperty - fun strictRunFilter(block: StrictRunFilterPluginConfiguration.() -> Unit) { - strictRunFilterConfiguration = StrictRunFilterPluginConfiguration().also(block) - } + /** + * Tests that have failed with stack traces that match that property wouldn't be rerun + * It applies to both failed and uncompleted tests + * This has higher priority than uncompletedRetriesQuota or amount of runs in StrictRunFilterPluginConfiguration + */ + val failFastFailureRegexes: ListProperty + + val uncompletedTestRetryQuota: Property + val usedStorageThresholdInPercents: Property + val testOutputTimeoutMillis: Property + val noDevicesTimeoutMillis: Property + + val installOptions: ListProperty + val instrumentationArgs: MapProperty + + val analyticsTracker: Property + val listener: Property + + fun initDefaults() { + poolingStrategy.initDefaults() + strictRunConfiguration.initDefaults() + + serialStrategy.convention(SerialStrategyConfiguration.AUTOMATIC) + usedStorageThresholdInPercents.convention(DEFAULT_USED_STORAGE_THRESHOLD_PERCENTS) + + autoGrantPermission.convention(DEFAULT_AUTO_GRANT_PERMISSION) + debug.convention(true) + ignoreFailures.convention(false) + strictMode.convention(false) - fun instrumentationArgs(block: MutableMap.() -> Unit) { - instrumentationArgs = mutableMapOf().also(block) + applicationPmClear.convention(DEFAULT_APPLICATION_PM_CLEAR) + testApplicationPmClear.convention(DEFAULT_TEST_APPLICATION_PM_CLEAR) } - //Groovy way - fun cache(closure: Closure<*>) { - cache = CachePluginConfiguration() - closure.delegate = cache - closure.call() + fun cache(action: Action) { + action.execute(cache) } - fun batchingStrategy(closure: Closure<*>) { - batchingStrategy = BatchingStrategyConfiguration() - closure.delegate = batchingStrategy - closure.call() + fun batchingStrategy(action: Action) { + action.execute(batchingStrategy) } - fun flakinessStrategy(closure: Closure<*>) { - flakinessStrategy = FlakinessStrategyConfiguration() - closure.delegate = flakinessStrategy - closure.call() + fun flakinessStrategy(action: Action) { + action.execute(flakinessStrategy) } - fun poolingStrategy(closure: Closure<*>) { - poolingStrategy = PoolingStrategyConfiguration() - closure.delegate = poolingStrategy - closure.call() + fun poolingStrategy(action: Action) { + action.execute(poolingStrategy) } - fun retryStrategy(closure: Closure<*>) { - retryStrategy = RetryStrategyConfiguration() - closure.delegate = retryStrategy - closure.call() + fun retryStrategy(action: Action) { + action.execute(retryStrategy) } - fun shardingStrategy(closure: Closure<*>) { - shardingStrategy = ShardingStrategyConfiguration() - closure.delegate = shardingStrategy - closure.call() + fun shardingStrategy(action: Action) { + action.execute(shardingStrategy) } - fun sortingStrategy(closure: Closure<*>) { - sortingStrategy = SortingStrategyConfiguration() - closure.delegate = sortingStrategy - closure.call() + fun sortingStrategy(action: Action) { + action.execute(sortingStrategy) } - fun filteringConfiguration(closure: Closure<*>) { - filteringConfiguration = FilteringPluginConfiguration() - closure.delegate = filteringConfiguration - closure.call() + fun filteringConfiguration(action: Action) { + action.execute(filteringConfiguration) } - fun strictRunFilter(closure: Closure<*>) { - strictRunFilterConfiguration = StrictRunFilterPluginConfiguration() - closure.delegate = strictRunFilterConfiguration - closure.call() + fun strictRunConfiguration(action: Action) { + action.execute(strictRunConfiguration) } - fun instrumentationArgs(closure: Closure<*>) { - instrumentationArgs = mutableMapOf() - closure.delegate = instrumentationArgs - closure.call() + companion object { + const val NAME = "marathon" } } diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonPlugin.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonPlugin.kt index 9313b18ba..2ac265eb1 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonPlugin.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonPlugin.kt @@ -31,7 +31,9 @@ class MarathonPlugin : Plugin { } private fun Project.configureRootProject() { - val marathonConfig = project.extensions.create("marathon", MarathonExtension::class.java) + val marathonConfig = extensions.create(MarathonExtension.NAME, MarathonExtension::class.java) + marathonConfig.initDefaults() + tasks.register(WORKER_TASK_NAME, MarathonWorkerRunTask::class.java) gradle.projectsEvaluated { diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/PoolingStrategyConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/PoolingStrategyConfiguration.kt index 6df9955d0..edc47afe4 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/PoolingStrategyConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/PoolingStrategyConfiguration.kt @@ -7,33 +7,33 @@ import com.malinskiy.marathon.execution.strategy.impl.pooling.parameterized.Comb import com.malinskiy.marathon.execution.strategy.impl.pooling.parameterized.ManufacturerPoolingStrategy import com.malinskiy.marathon.execution.strategy.impl.pooling.parameterized.ModelPoolingStrategy import com.malinskiy.marathon.execution.strategy.impl.pooling.parameterized.OperatingSystemVersionPoolingStrategy +import org.gradle.api.provider.Property -class PoolingStrategyConfiguration { - var operatingSystem: Boolean? = null - var abi: Boolean? = null - var manufacturer: Boolean? = null - var model: Boolean? = null +interface PoolingStrategyConfiguration { + val operatingSystem: Property + val abi: Property + val manufacturer: Property + val model: Property + + fun initDefaults() { + operatingSystem.convention(false) + abi.convention(false) + manufacturer.convention(false) + model.convention(false) + } } -fun PoolingStrategyConfiguration.toStrategy(): PoolingStrategy { - if (listOf(operatingSystem, abi, manufacturer, model).all { it == null || it == false }) { - return OmniPoolingStrategy() +internal fun PoolingStrategyConfiguration.toStrategy(): PoolingStrategy { + val strategies = mutableListOf() + when { + operatingSystem.get() -> strategies.add(OperatingSystemVersionPoolingStrategy()) + abi.get() -> strategies.add(AbiPoolingStrategy()) + manufacturer.get() -> strategies.add(ManufacturerPoolingStrategy()) + model.get() -> strategies.add(ModelPoolingStrategy()) + } + return if (strategies.isNotEmpty()) { + ComboPoolingStrategy(strategies) } else { - val strategies = mutableListOf() - when { - operatingSystem != null && operatingSystem == true -> { - strategies.add(OperatingSystemVersionPoolingStrategy()) - } - abi != null && abi == true -> { - strategies.add(AbiPoolingStrategy()) - } - manufacturer != null && manufacturer == true -> { - strategies.add(ManufacturerPoolingStrategy()) - } - model != null && model == true -> { - strategies.add(ModelPoolingStrategy()) - } - } - return ComboPoolingStrategy(strategies) + OmniPoolingStrategy() } } diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/RetryStrategyConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/RetryStrategyConfiguration.kt index bb2bdf67f..8d537141d 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/RetryStrategyConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/RetryStrategyConfiguration.kt @@ -3,32 +3,35 @@ package com.malinskiy.marathon import com.malinskiy.marathon.execution.strategy.RetryStrategy import com.malinskiy.marathon.execution.strategy.impl.retry.NoRetryStrategy import com.malinskiy.marathon.execution.strategy.impl.retry.fixedquota.FixedQuotaRetryStrategy -import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested -open class RetryStrategyConfiguration { - var fixedQuota: FixedQuotaRetryStrategyConfiguration? = null +interface RetryStrategyConfiguration { + @get:Nested + val fixedQuota: FixedQuotaRetryStrategyConfiguration - fun fixedQuota(block: FixedQuotaRetryStrategyConfiguration.() -> Unit) { - fixedQuota = FixedQuotaRetryStrategyConfiguration().also(block) - } - - fun fixedQuota(closure: Closure<*>) { - fixedQuota = FixedQuotaRetryStrategyConfiguration() - closure.delegate = fixedQuota - closure.call() + fun fixedQuota(action: Action) { + fixedQuota.initDefaults() + action.execute(fixedQuota) } } -private const val DEFAULT_TOTAL_ALLOWED_RETRY_QUOTA = 200 -private const val DEFAULT_RETRY_PER_TEST_QUOTA = 3 +interface FixedQuotaRetryStrategyConfiguration { + val totalAllowedRetryQuota: Property + val retryPerTestQuota: Property -open class FixedQuotaRetryStrategyConfiguration { - var totalAllowedRetryQuota: Int = DEFAULT_TOTAL_ALLOWED_RETRY_QUOTA - var retryPerTestQuota: Int = DEFAULT_RETRY_PER_TEST_QUOTA + fun initDefaults() { + totalAllowedRetryQuota.convention(200) + retryPerTestQuota.convention(3) + } } -fun RetryStrategyConfiguration.toStrategy(): RetryStrategy { - return fixedQuota?.let { - FixedQuotaRetryStrategy(it.totalAllowedRetryQuota, it.retryPerTestQuota) - } ?: NoRetryStrategy() -} +internal fun RetryStrategyConfiguration.toStrategy(): RetryStrategy = + if (fixedQuota.totalAllowedRetryQuota.isPresent) fixedQuota.toStrategy() else NoRetryStrategy() + +private fun FixedQuotaRetryStrategyConfiguration.toStrategy(): FixedQuotaRetryStrategy = + FixedQuotaRetryStrategy( + totalAllowedRetryQuota = totalAllowedRetryQuota.get(), + retryPerTestQuota = retryPerTestQuota.get() + ) diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ShardingStrategyConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ShardingStrategyConfiguration.kt index 8dd0be7da..ea71a3a39 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ShardingStrategyConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ShardingStrategyConfiguration.kt @@ -3,26 +3,30 @@ package com.malinskiy.marathon import com.malinskiy.marathon.execution.strategy.ShardingStrategy import com.malinskiy.marathon.execution.strategy.impl.sharding.CountShardingStrategy import com.malinskiy.marathon.execution.strategy.impl.sharding.ParallelShardingStrategy -import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested -class ShardingStrategyConfiguration { - var countSharding: CountShardingStrategyConfiguration? = null +interface ShardingStrategyConfiguration { + @get:Nested + val countSharding: CountShardingStrategyConfiguration - fun countSharding(closure: Closure<*>) { - countSharding = CountShardingStrategyConfiguration() - closure.delegate = countSharding - closure.call() + fun countSharding(action: Action) { + countSharding.initDefaults() + action.execute(countSharding) } +} + +interface CountShardingStrategyConfiguration { + val count: Property - fun countSharding(block: CountShardingStrategyConfiguration.() -> Unit) { - countSharding = CountShardingStrategyConfiguration().also(block) + fun initDefaults() { + count.convention(1) } } -class CountShardingStrategyConfiguration { - var count = 1 -} +internal fun ShardingStrategyConfiguration.toStrategy(): ShardingStrategy = + if (countSharding.count.isPresent) countSharding.toStrategy() else ParallelShardingStrategy() -fun ShardingStrategyConfiguration.toStrategy(): ShardingStrategy = countSharding?.let { - CountShardingStrategy(it.count) -} ?: ParallelShardingStrategy() +private fun CountShardingStrategyConfiguration.toStrategy(): CountShardingStrategy = + CountShardingStrategy(count.get()) diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/SortingStrategyConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/SortingStrategyConfiguration.kt index 1ced6a3e7..a683c69bd 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/SortingStrategyConfiguration.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/SortingStrategyConfiguration.kt @@ -5,65 +5,86 @@ import com.malinskiy.marathon.execution.strategy.impl.sorting.ExecutionTimeSorti import com.malinskiy.marathon.execution.strategy.impl.sorting.NoSortingStrategy import com.malinskiy.marathon.execution.strategy.impl.sorting.RandomOrderSortingStrategy import com.malinskiy.marathon.execution.strategy.impl.sorting.SuccessRateSortingStrategy -import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested +import java.time.Duration import java.time.Instant -import java.time.temporal.ChronoUnit -class SortingStrategyConfiguration { - var executionTime: ExecutionTimeSortingStrategyConfiguration? = null - var successRate: SuccessRateSortingStrategyConfiguration? = null - var randomOrder: RandomOrderStrategyConfiguration? = null +interface SortingStrategyConfiguration { + @get:Nested + val executionTime: ExecutionTimeSortingStrategyConfiguration - fun executionTime(block: ExecutionTimeSortingStrategyConfiguration.() -> Unit) { - executionTime = ExecutionTimeSortingStrategyConfiguration().also(block) - } + @get:Nested + val successRate: SuccessRateSortingStrategyConfiguration - fun executionTime(closure: Closure<*>) { - executionTime = ExecutionTimeSortingStrategyConfiguration() - closure.delegate = executionTime - closure.call() - } + @get:Nested + val randomOrder: RandomOrderStrategyConfiguration - fun randomOrder(block: RandomOrderStrategyConfiguration.() -> Unit) { - randomOrder = RandomOrderStrategyConfiguration.also(block) + fun executionTime(action: Action) { + executionTime.initDefaults() + action.execute(executionTime) } - fun randomOrder(closure: Closure<*>) { - randomOrder = RandomOrderStrategyConfiguration - closure.delegate = randomOrder - closure.call() + fun successRate(action: Action) { + successRate.initDefaults() + action.execute(successRate) } - fun successRate(block: SuccessRateSortingStrategyConfiguration.() -> Unit) { - successRate = SuccessRateSortingStrategyConfiguration().also(block) + fun randomOrder(action: Action) { + randomOrder.initDefaults() + action.execute(randomOrder) } +} + +interface ExecutionTimeSortingStrategyConfiguration { + val percentile: Property + val timeLimit: Property - fun successRate(closure: Closure<*>) { - successRate = SuccessRateSortingStrategyConfiguration() - closure.delegate = successRate - closure.call() + fun initDefaults() { + percentile.convention(DEFAULT_PERCENTILE) + timeLimit.convention(Duration.ofDays(DEFAULT_DAYS_COUNT)) } } -private const val DEFAULT_PERCENTILE = 90.0 -const val DEFAULT_DAYS_COUNT = 30L - -object RandomOrderStrategyConfiguration +interface SuccessRateSortingStrategyConfiguration { + val limit: Property + val ascending: Property -class ExecutionTimeSortingStrategyConfiguration { - var percentile: Double = DEFAULT_PERCENTILE - var timeLimit: Instant = Instant.now().minus(DEFAULT_DAYS_COUNT, ChronoUnit.DAYS) + fun initDefaults() { + limit.convention(Duration.ofDays(DEFAULT_DAYS_COUNT)) + ascending.convention(false) + } } -class SuccessRateSortingStrategyConfiguration { - var limit: Instant = Instant.now().minus(DEFAULT_DAYS_COUNT, ChronoUnit.DAYS) - var ascending: Boolean = false +interface RandomOrderStrategyConfiguration { + @Suppress("PropertyName", "VariableNaming") + val _initialized: Property + + fun initDefaults() { + _initialized.convention(0) + } } -fun SortingStrategyConfiguration.toStrategy(): SortingStrategy = executionTime?.let { - ExecutionTimeSortingStrategy(it.percentile, it.timeLimit) -} ?: successRate?.let { - SuccessRateSortingStrategy(it.limit, it.ascending) -} ?: randomOrder?.let { - RandomOrderSortingStrategy() -} ?: NoSortingStrategy() +internal fun SortingStrategyConfiguration.toStrategy(): SortingStrategy = + when { + executionTime.percentile.isPresent -> executionTime.toStrategy() + successRate.limit.isPresent -> successRate.toStrategy() + randomOrder._initialized.isPresent -> RandomOrderSortingStrategy() + else -> NoSortingStrategy() + } + +private fun ExecutionTimeSortingStrategyConfiguration.toStrategy(): ExecutionTimeSortingStrategy = + ExecutionTimeSortingStrategy( + percentile = percentile.get(), + timeLimit = Instant.now().minus(timeLimit.get()) + ) + +private fun SuccessRateSortingStrategyConfiguration.toStrategy(): SuccessRateSortingStrategy = + SuccessRateSortingStrategy( + timeLimit = Instant.now().minus(limit.get()), + ascending = ascending.get() + ) + +private const val DEFAULT_PERCENTILE = 90.0 +private const val DEFAULT_DAYS_COUNT = 30L diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/StrictRunFilterPluginConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/StrictRunFilterPluginConfiguration.kt deleted file mode 100644 index 6465fc601..000000000 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/StrictRunFilterPluginConfiguration.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.malinskiy.marathon - -import com.malinskiy.marathon.execution.StrictRunFilterConfiguration -import com.malinskiy.marathon.execution.TestFilter -import groovy.lang.Closure - -open class StrictRunFilterPluginConfiguration { - //groovy - var groovyFilter: FilterWrapper? = null - var runs: Int = 1 - - fun filter(closure: Closure<*>) { - groovyFilter = FilterWrapper() - closure.delegate = groovyFilter - closure.call() - } - - //kts - var filter: MutableCollection = mutableListOf() - fun filter(block: MutableCollection.() -> Unit) { - filter.also(block) - } -} - -fun StrictRunFilterPluginConfiguration.toStrictRunFilterConfiguration(): StrictRunFilterConfiguration { - if (groovyFilter != null) { - val filter = groovyFilter?.toList() ?: emptyList() - return StrictRunFilterConfiguration(filter, runs) - } - return StrictRunFilterConfiguration(filter, runs) -} diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/StrictRunPluginConfiguration.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/StrictRunPluginConfiguration.kt new file mode 100644 index 000000000..98e7dce5a --- /dev/null +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/StrictRunPluginConfiguration.kt @@ -0,0 +1,24 @@ +package com.malinskiy.marathon + +import com.malinskiy.marathon.execution.StrictRunConfiguration +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Nested + +interface StrictRunPluginConfiguration { + val runs: Property + + @get:Nested + val filter: FilterWrapper + + fun initDefaults() { + runs.convention(1) + } + + fun filter(action: Action) { + action.execute(filter) + } +} + +internal fun StrictRunPluginConfiguration.toStrictRunConfiguration(): StrictRunConfiguration = + StrictRunConfiguration(filter.toList(), runs.get()) diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 6fd1059ef..bbf84252c 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -4,3 +4,7 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.marathon) } + +marathon { + ignoreFailures = true +} diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidConfiguration.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidConfiguration.kt index 1e26c4c55..02cd68fd2 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidConfiguration.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/AndroidConfiguration.kt @@ -10,7 +10,6 @@ import java.io.File const val DEFAULT_AUTO_GRANT_PERMISSION = false const val DEFAULT_APPLICATION_PM_CLEAR = false const val DEFAULT_TEST_APPLICATION_PM_CLEAR = false -const val DEFAULT_INSTALL_OPTIONS = "" const val DEFAULT_USED_STORAGE_THRESHOLD_PERCENTS = 85 data class AndroidConfiguration( @@ -20,7 +19,7 @@ data class AndroidConfiguration( val instrumentationArgs: Map = emptyMap(), val applicationPmClear: Boolean = DEFAULT_APPLICATION_PM_CLEAR, val testApplicationPmClear: Boolean = DEFAULT_TEST_APPLICATION_PM_CLEAR, - val installOptions: String = DEFAULT_INSTALL_OPTIONS, + val installOptions: List = emptyList(), val preferableRecorderType: DeviceFeature? = null, val serialStrategy: SerialStrategy = SerialStrategy.AUTOMATIC, val usedStorageThresholdInPercents: Int = DEFAULT_USED_STORAGE_THRESHOLD_PERCENTS 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 ab64483b1..c22a79b78 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 @@ -134,16 +134,11 @@ class AndroidAppInstaller( } private fun optionalParams(device: AndroidDevice): String { - val options = if (device.apiLevel >= MARSHMALLOW_VERSION_CODE && androidConfiguration.autoGrantPermission) { - "-g -r" - } else { - "-r" - } - - return if (androidConfiguration.installOptions.isNotEmpty()) { - "$options ${androidConfiguration.installOptions}" - } else { - options + val options = mutableListOf("-r") + if (device.apiLevel >= MARSHMALLOW_VERSION_CODE && androidConfiguration.autoGrantPermission) { + options += "-g" } + options += androidConfiguration.installOptions + return options.joinToString(" ") } } 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 e62fa9846..ba9592c11 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 @@ -50,7 +50,7 @@ class AndroidDeviceTestRunnerSpek : Spek( val output = File("") val configuration = Configuration( outputDir = output, - customAnalyticsTracker = null, + cache = null, poolingStrategy = null, shardingStrategy = null, sortingStrategy = null, @@ -58,11 +58,10 @@ class AndroidDeviceTestRunnerSpek : Spek( flakinessStrategy = null, retryStrategy = null, filteringConfiguration = null, - strictRunFilterConfiguration = null, - cache = null, + strictRunConfiguration = null, + debug = null, ignoreFailures = null, strictMode = null, - listener = null, uncompletedTestRetryQuota = null, testClassRegexes = null, includeSerialRegexes = null, @@ -71,7 +70,8 @@ class AndroidDeviceTestRunnerSpek : Spek( failFastFailureRegexes = null, testOutputTimeoutMillis = null, noDevicesTimeoutMillis = null, - debug = null, + analyticsTracker = null, + listener = null, vendorConfiguration = AndroidConfiguration( adbPath = File("adb"), implementationModules = emptyList() 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 bff3bc2f0..d36a2bf7e 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 @@ -6,7 +6,7 @@ import com.malinskiy.marathon.execution.CacheConfiguration import com.malinskiy.marathon.execution.Configuration import com.malinskiy.marathon.execution.FilteringConfiguration import com.malinskiy.marathon.execution.MarathonListener -import com.malinskiy.marathon.execution.StrictRunFilterConfiguration +import com.malinskiy.marathon.execution.StrictRunConfiguration import com.malinskiy.marathon.execution.strategy.BatchingStrategy import com.malinskiy.marathon.execution.strategy.FlakinessStrategy import com.malinskiy.marathon.execution.strategy.PoolingStrategy @@ -22,29 +22,29 @@ fun configuration(block: ConfigurationFactory.() -> Unit = {}) = ConfigurationFa class ConfigurationFactory { var outputDir = Files.createTempDirectory("test-run").toFile() - var vendorConfiguration = TestVendorConfiguration() - var debug: Boolean? = null - var batchingStrategy: BatchingStrategy? = null - var customAnalyticsTracker: Tracker? = null - var excludeSerialRegexes: List? = null - var ignoreFailureRegexes: List? = null - var failFastFailureRegexes: List? = null - var strictMode: Boolean? = null - var uncompletedTestRetryQuota: Int? = null - var filteringConfiguration: FilteringConfiguration? = null - var strictRunFilterConfiguration: StrictRunFilterConfiguration? = null - var listener: MarathonListener? = null - var flakinessStrategy: FlakinessStrategy? = null var cache: CacheConfiguration? = null - var ignoreFailures: Boolean? = null - var includeSerialRegexes: List? = null var poolingStrategy: PoolingStrategy? = null - var retryStrategy: RetryStrategy? = null var shardingStrategy: ShardingStrategy? = null var sortingStrategy: SortingStrategy? = null + var batchingStrategy: BatchingStrategy? = null + var flakinessStrategy: FlakinessStrategy? = null + var retryStrategy: RetryStrategy? = null + var filteringConfiguration: FilteringConfiguration? = null + var strictRunConfiguration: StrictRunConfiguration? = null + var debug: Boolean? = null + var ignoreFailures: Boolean? = null + var strictMode: Boolean? = null + var uncompletedTestRetryQuota: Int? = null + var includeSerialRegexes: List? = null + var excludeSerialRegexes: List? = null var testClassRegexes: Collection? = null + var ignoreFailureRegexes: List? = null + var failFastFailureRegexes: List? = null var testOutputTimeoutMillis: Long? = null var noDevicesTimeoutMillis: Long? = null + var analyticsTracker: Tracker? = null + var listener: MarathonListener? = null + var vendorConfiguration = TestVendorConfiguration() fun tests(block: () -> List) { val testParser = vendorConfiguration.testParser @@ -59,7 +59,7 @@ class ConfigurationFactory { fun build(): Configuration = Configuration( outputDir = outputDir, - customAnalyticsTracker = customAnalyticsTracker, + cache = cache, poolingStrategy = poolingStrategy, shardingStrategy = shardingStrategy, sortingStrategy = sortingStrategy, @@ -67,20 +67,20 @@ class ConfigurationFactory { flakinessStrategy = flakinessStrategy, retryStrategy = retryStrategy, filteringConfiguration = filteringConfiguration, - strictRunFilterConfiguration = strictRunFilterConfiguration, - cache = cache, + strictRunConfiguration = strictRunConfiguration, + debug = debug, ignoreFailures = ignoreFailures, strictMode = strictMode, - listener = listener, uncompletedTestRetryQuota = uncompletedTestRetryQuota, - testClassRegexes = testClassRegexes, includeSerialRegexes = includeSerialRegexes, excludeSerialRegexes = excludeSerialRegexes, + testClassRegexes = testClassRegexes, ignoreFailureRegexes = ignoreFailureRegexes, failFastFailureRegexes = failFastFailureRegexes, testOutputTimeoutMillis = testOutputTimeoutMillis, noDevicesTimeoutMillis = noDevicesTimeoutMillis, - debug = debug, + analyticsTracker = analyticsTracker, + listener = listener, vendorConfiguration = vendorConfiguration ) }