Skip to content

Commit

Permalink
Merge branch 'develop' into feature/android_test_plugin_support
Browse files Browse the repository at this point in the history
  • Loading branch information
Malinskiy committed Nov 15, 2023
2 parents 3c3bb1a + 8d3ca41 commit a61225d
Show file tree
Hide file tree
Showing 231 changed files with 3,534 additions and 7,022 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ iOS:
```yaml
name: "My application"
outputDir: "derived-data/Marathon"
testClassRegexes: ["^((?!Abstract).)*Tests$"]
vendorConfiguration:
type: "iOS"
bundle:
Expand Down Expand Up @@ -137,7 +136,8 @@ See [contributing docs][contributing]

## License

See [LICENSE][LICENSE]
Marathon codebase is GPL 2.0 [LICENSE][LICENSE] with following optional components under specific licenses:
* [libxctest-parser][libxctest-parser-license]

<!--
Repo References
Expand All @@ -161,5 +161,6 @@ Link References
[contributing]:https://docs.marathonlabs.io/intro/contribute
[prs]:http://makeapullrequest.com "Make a Pull Request (external link) ➶"
[LICENSE]:https://github.com/MarathonLabs/marathon/blob/-/LICENSE
[libxctest-parser-license]: https://github.com/MarathonLabs/marathon/blob/-/vendor/vendor-ios/src/main/resources/EULA.md

[marathon-cloud]:https://marathonlabs.io
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ sealed class Event {
) : Event()

data class Executed(
val seconds: Long
val seconds: Long,
val success: Boolean,
val flakinessSeconds: Long,
val durationSeconds: Long,
) : Event()
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,22 @@ class GrafanaCloud : UsageTracker {
append(
events.map { event ->
when (event) {
is Event.Devices -> Metric(name = "testing.device", value = event.total, tags = tags)
is Event.Executed -> Metric(name = "testing.duration", value = event.seconds, tags = tags)
is Event.TestsTotal -> Metric(name = "testing.test", value = event.total, tags = tags)
is Event.TestsRun -> Metric(name = "testing.executed", value = event.value, tags = tags)
is Event.Devices ->
setOf(Metric(name = "testing.device", value = event.total, tags = tags))

is Event.Executed -> {
setOf(
Metric(name = "testing.duration", value = event.seconds, tags = tags),
Metric(name = "testing.flakiness", value = event.flakinessSeconds, tags = tags),
Metric(name = "testing.result", value = if (event.success) 1 else 0, tags = tags),
Metric(name = "testing.duration.run", value = event.durationSeconds, tags = tags),
)
}

is Event.TestsTotal -> setOf(Metric(name = "testing.test", value = event.total, tags = tags))
is Event.TestsRun -> setOf(Metric(name = "testing.executed", value = event.value, tags = tags))
}
}.joinToString(separator = ",") { it.toJson() }
}.flatten().joinToString(separator = ",") { it.toJson() }
)
append("]")
}.toString()
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ buildscript {


plugins {
id("io.gitlab.arturbosch.detekt") version "1.23.1"
id("com.github.ben-manes.versions") version "0.47.0"
id("io.gitlab.arturbosch.detekt") version "1.23.3"
id("com.github.ben-manes.versions") version "0.49.0"
}

configure<DetektExtension> {
Expand Down
6 changes: 3 additions & 3 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repositories {
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10")
implementation("com.squareup:kotlinpoet:1.12.0")
implementation("com.google.code.gson:gson:2.10")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10")
implementation("com.squareup:kotlinpoet:1.14.2")
implementation("com.google.code.gson:gson:2.10.1")
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/ProjectExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.gradle.kotlin.dsl.withType
import org.gradle.testing.jacoco.tasks.JacocoReport
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

fun Project.setupKotlinCompiler(jvmTarget: String = "1.8") {
fun Project.setupKotlinCompiler(jvmTarget: String = "11") {
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = jvmTarget
kotlinOptions.apiVersion = "1.5"
Expand Down
48 changes: 24 additions & 24 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
object Versions {
val marathon = System.getenv("GIT_TAG_NAME") ?: "0.8.5"

val kotlin = "1.8.20"
val kotlin = "1.9.10"
val coroutines = "1.7.3"
val coroutinesTest = coroutines

val androidCommon = "31.1.0"
val adam = "0.5.1"
val androidCommon = "31.1.3"
val adam = "0.5.2"
val dexTestParser = "2.3.4"
val kotlinLogging = "3.0.5"
val logbackClassic = "1.4.8"
val logbackClassic = "1.4.11"
val axmlParser = "1.0"
val bugsnag = "3.7.0"
val bugsnag = "3.7.1"

val junitGradle = "1.2.0"
val androidGradleVersion = "7.4.1"
val gradlePluginPublish = "1.2.0"
val androidGradleVersion = "8.1.3"
val gradlePluginPublish = "1.2.1"
val gradlePluginShadow = "8.1.1"

val junit5 = "5.10.0"
val junit5 = "5.10.1"
val kluent = "1.73"

val kakao = "3.0.2"
Expand All @@ -27,37 +27,37 @@ object Versions {
val espressoRunner = "1.0.1"
val junit = "4.13.2"
val gson = "2.10.1"
val apacheCommonsText = "1.10.0"
val apacheCommonsText = "1.11.0"
val apacheCommonsIO = "2.11.0"
val apacheCommonsCodec = "1.15"
val okhttp = "4.11.0"
val okhttp = "4.12.0"
val influxDbClient = "2.23"
val influxDb2Client = "6.10.0"
val clikt = "4.1.0"
val jacksonDatabind = "2.15.2"
val clikt = "4.2.1"
val jacksonDatabind = "2.15.3"
val jacksonKotlin = jacksonDatabind
val jacksonYaml = jacksonDatabind
val jacksonJSR310 = jacksonDatabind
val jacksonAnnotations = jacksonDatabind
val ddPlist = "1.27"
val guava = "32.1.1-jre"
val rsync4j = "3.2.7-1"
val sshj = "0.35.0"
val guava = "32.1.3-jre"
val rsync4j = "3.2.7-5"
val sshj = "0.37.0"
val kotlinProcess = "1.4.1"
val testContainers = "1.18.3"
val testContainers = "1.19.1"
val jupiterEngine = junit5
val jansi = "2.4.0"
val jansi = "2.4.1"
val scalr = "4.2"
val allureTestFilter = "2.23.0"
val allureJava = "2.23.0"
val allureTestFilter = "2.24.0"
val allureJava = "2.24.0"
val allureKotlin = "2.4.0"
val allureEnvironment = "1.0.0"
val mockitoKotlin = "2.2.0"
val dokka = "1.8.10"
val koin = "3.4.3"
val mockitoKotlin = "5.1.0"
val dokka = "1.9.10"
val koin = "3.5.0"
val jsonAssert = "1.5.1"
val xmlUnit = "2.9.1"
val assertk = "0.26.1"
val assertk = "0.27.0"
}

object BuildPlugins {
Expand Down Expand Up @@ -114,7 +114,7 @@ object TestLibraries {

val junit = "junit:junit:${Versions.junit}"

val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:${Versions.mockitoKotlin}"
val mockitoKotlin = "org.mockito.kotlin:mockito-kotlin:${Versions.mockitoKotlin}"
val jupiterEngine = "org.junit.jupiter:junit-jupiter-engine:${Versions.jupiterEngine}"
val koin = "io.insert-koin:koin-test:${Versions.koin}"
val jsonAssert = "org.skyscreamer:jsonassert:${Versions.jsonAssert}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ sealed class AnalyticsConfiguration {
val default: RetentionPolicyConfiguration = RetentionPolicyConfiguration("rpMarathon", "30d", "30m", 2, true)
}
}

/**
* Hide sensitive information
*/
override fun toString(): String {
return "InfluxDbConfiguration(url='*****', user='*****', password='*****', dbName='$dbName', retentionPolicyConfiguration=$retentionPolicyConfiguration)"
}
}

data class InfluxDb2Configuration(
Expand All @@ -51,6 +58,12 @@ sealed class AnalyticsConfiguration {
val default: RetentionPolicyConfiguration = RetentionPolicyConfiguration(86400 * 30, 0L)
}
}

override fun toString(): String {
return "InfluxDb2Configuration(url='*****', token='*****', organization='$organization', bucket='$bucket', retentionPolicyConfiguration=$retentionPolicyConfiguration)"
}


}

data class GraphiteConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ data class Configuration private constructor(
val isCodeCoverageEnabled: Boolean,
val uncompletedTestRetryQuota: Int,

val testClassRegexes: Collection<Regex>,
val includeSerialRegexes: Collection<Regex>,
val excludeSerialRegexes: Collection<Regex>,

Expand Down Expand Up @@ -68,7 +67,6 @@ data class Configuration private constructor(
"ignoreFailures" to ignoreFailures.toString(),
"isCodeCoverageEnabled" to isCodeCoverageEnabled.toString(),
"executionStrategy" to executionStrategy.toString(),
"testClassRegexes" to testClassRegexes.toString(),
"includeSerialRegexes" to includeSerialRegexes.toString(),
"excludeSerialRegexes" to excludeSerialRegexes.toString(),
"testBatchTimeoutMillis" to testBatchTimeoutMillis.toString(),
Expand Down Expand Up @@ -100,8 +98,6 @@ data class Configuration private constructor(
if (isCodeCoverageEnabled != other.isCodeCoverageEnabled) return false
if (executionStrategy != other.executionStrategy) return false
if (uncompletedTestRetryQuota != other.uncompletedTestRetryQuota) return false
//For testing we need to compare configuration instances. Unfortunately Regex equality is broken so need to map it to String
if (testClassRegexes.map { it.pattern } != other.testClassRegexes.map { it.pattern }) return false
if (includeSerialRegexes.map { it.pattern } != other.includeSerialRegexes.map { it.pattern }) return false
if (excludeSerialRegexes.map { it.pattern } != other.excludeSerialRegexes.map { it.pattern }) return false
if (testBatchTimeoutMillis != other.testBatchTimeoutMillis) return false
Expand Down Expand Up @@ -132,7 +128,6 @@ data class Configuration private constructor(
result = 31 * result + isCodeCoverageEnabled.hashCode()
result = 31 * result + executionStrategy.hashCode()
result = 31 * result + uncompletedTestRetryQuota
result = 31 * result + testClassRegexes.hashCode()
result = 31 * result + includeSerialRegexes.hashCode()
result = 31 * result + excludeSerialRegexes.hashCode()
result = 31 * result + testBatchTimeoutMillis.hashCode()
Expand Down Expand Up @@ -163,7 +158,6 @@ data class Configuration private constructor(
var executionStrategy: ExecutionStrategyConfiguration = ExecutionStrategyConfiguration(),
var uncompletedTestRetryQuota: Int = Integer.MAX_VALUE,

var testClassRegexes: Collection<Regex> = listOf(Regex("^((?!Abstract).)*Test[s]*$")),
var includeSerialRegexes: Collection<Regex> = emptyList(),
var excludeSerialRegexes: Collection<Regex> = emptyList(),

Expand Down Expand Up @@ -197,7 +191,6 @@ data class Configuration private constructor(
isCodeCoverageEnabled = isCodeCoverageEnabled,
executionStrategy = executionStrategy,
uncompletedTestRetryQuota = uncompletedTestRetryQuota,
testClassRegexes = testClassRegexes,
includeSerialRegexes = includeSerialRegexes,
excludeSerialRegexes = excludeSerialRegexes,
testBatchTimeoutMillis = testBatchTimeoutMillis,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class LogicalConfigurationValidator : ConfigurationValidator {
is VendorConfiguration.IOSConfiguration -> {
configuration.vendorConfiguration.validate()
}
is VendorConfiguration.AndroidConfiguration -> {
configuration.vendorConfiguration.validate()
}

else -> Unit
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.malinskiy.marathon.config.serialization
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
Expand Down Expand Up @@ -41,6 +42,7 @@ class ConfigurationFactory(
.build()
)
registerModule(JavaTimeModule())
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
},
private val environmentVariableSubstitutor: StringSubstitutor = StringSubstitutor(StringLookupFactory.INSTANCE.environmentVariableStringLookup()),
private val analyticsTracking: Boolean? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.malinskiy.marathon.config.serialization

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
Expand Down Expand Up @@ -29,6 +30,7 @@ class MutableConfigurationFactory {
.build()
)
registerModule(JavaTimeModule())
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
}

fun parse(value: String) = mapper.readValue(value, Configuration.Builder::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.malinskiy.marathon.config.strategy

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import java.time.Instant
Expand All @@ -17,9 +18,9 @@ import java.time.Instant
sealed class BatchingStrategyConfiguration {
data class FixedSizeBatchingStrategyConfiguration(
val size: Int,
val durationMillis: Long? = null,
val percentile: Double? = null,
val timeLimit: Instant? = null,
@JsonInclude(JsonInclude.Include.NON_NULL) val durationMillis: Long? = null,
@JsonInclude(JsonInclude.Include.NON_NULL) val percentile: Double? = null,
@JsonInclude(JsonInclude.Include.NON_NULL) val timeLimit: Instant? = null,
val lastMileLength: Int = 0
) : BatchingStrategyConfiguration()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,26 @@ sealed class VendorConfiguration {
@JsonProperty("disableWindowAnimation") val disableWindowAnimation: Boolean = DEFAULT_DISABLE_WINDOW_ANIMATION,
) : VendorConfiguration() {
fun safeAndroidSdk(): File = androidSdk ?: throw ConfigurationException("No android SDK path specified")

fun validate() {
validateFile(applicationOutput)
validateFile(testApplicationOutput)
splitApks?.forEach { validateFile(it) }
extraApplicationsOutput?.forEach { validateFile(it) }
outputs?.forEach {
validateFile(it.application)
validateFile(it.testApplication)
it.splitApks?.forEach { apk -> validateFile(apk) }
it.extraApplications?.forEach { apk -> validateFile(apk) }
}
}

private fun validateFile(file: File?) {
if (file != null) {
if (!file.exists()) throw ConfigurationException("${file.absolutePath} does not exist")
if (!file.isFile) throw ConfigurationException("${file.absolutePath} must be a regular file")
}
}
}

class AndroidConfigurationBuilder {
Expand Down Expand Up @@ -142,6 +162,8 @@ sealed class VendorConfiguration {
@JsonProperty("compactOutput") val compactOutput: Boolean = false,
@JsonProperty("rsync") val rsync: RsyncConfiguration = RsyncConfiguration(),
@JsonProperty("xcodebuildTestArgs") val xcodebuildTestArgs: Map<String, String> = emptyMap(),
@JsonProperty("dataContainerClear") val dataContainerClear: Boolean = false,
@JsonProperty("testParserConfiguration") val testParserConfiguration: com.malinskiy.marathon.config.vendor.ios.TestParserConfiguration = com.malinskiy.marathon.config.vendor.ios.TestParserConfiguration.NmTestParserConfiguration(),

@JsonProperty("signing") val signing: SigningConfiguration = SigningConfiguration(),
) : VendorConfiguration() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.malinskiy.marathon.config.vendor.ios

import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo

@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes(
JsonSubTypes.Type(value = TestParserConfiguration.NmTestParserConfiguration::class, name = "nm"),
JsonSubTypes.Type(value = TestParserConfiguration.XCTestParserConfiguration::class, name = "xctest"),
)
sealed class TestParserConfiguration {
data class NmTestParserConfiguration(val testClassRegexes: Collection<Regex> = listOf(Regex("^((?!Abstract).)*Test[s]*$"))) : TestParserConfiguration() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as NmTestParserConfiguration

//For testing we need to compare configuration instances. Unfortunately Regex equality is broken so need to map it to String
return testClassRegexes.map { it.pattern } == other.testClassRegexes.map { it.pattern }
}

override fun hashCode(): Int {
return testClassRegexes.hashCode()
}
}
object XCTestParserConfiguration : TestParserConfiguration()
}
Loading

0 comments on commit a61225d

Please sign in to comment.