Skip to content

Commit

Permalink
Merge pull request #131 from Malinskiy/feature/android_screenshot
Browse files Browse the repository at this point in the history
Add support for taking screenshots on Android
  • Loading branch information
Malinskiy authored Dec 11, 2018
2 parents be4c3c3 + 4310cde commit decd74f
Show file tree
Hide file tree
Showing 65 changed files with 1,224 additions and 365 deletions.
8 changes: 8 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ plugins {
`kotlin-dsl`
}

kotlinDslPluginOptions {
experimentalWarning.set(false)
}

repositories {
jcenter()
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.10")
}
8 changes: 6 additions & 2 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
object Versions {
val marathon = System.getenv("DEPLOY_VERSION_OVERRIDE") ?: "0.2.2"

val kotlin = "1.2.61"
val coroutines = "0.25.0"
val kotlin = "1.3.10"
val coroutines = "1.0.1"

val ddmlib = "26.2.0"
val dexTestParser = "2.0.0"
Expand Down Expand Up @@ -37,6 +37,8 @@ object Versions {
val sshj = "0.26.0"
val testContainers = "1.9.1"
val jupiterEngine = "5.1.0"
val scalr = "4.2"
val mockitoKotlin = "2.0.0"
}

object BuildPlugins {
Expand Down Expand Up @@ -70,6 +72,7 @@ object Libraries {
val guava = "com.google.guava:guava:${Versions.guava}"
val rsync4j = "com.github.fracpete:rsync4j-all:${Versions.rsync4j}"
val sshj = "com.hierynomus:sshj:${Versions.sshj}"
val scalr = "org.imgscalr:imgscalr-lib:${Versions.scalr}"
}

object TestLibraries {
Expand All @@ -85,6 +88,7 @@ object TestLibraries {
val espressoContrib = "com.android.support.test.espresso:espresso-contrib:${Versions.espresso}"
val espressoIntents = "com.android.support.test.espresso:espresso-intents:${Versions.espresso}"
val junit = "junit:junit:${Versions.junit}"
val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:${Versions.mockitoKotlin}"
val jupiterEngine = "org.junit.jupiter:junit-jupiter-engine:${Versions.jupiterEngine}"

val testContainers = "org.testcontainers:testcontainers:${Versions.testContainers}"
Expand Down
21 changes: 12 additions & 9 deletions cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ plugins {
id("de.fuerstenau.buildconfig") version "1.1.8"
}

val debugCoroutines = false

application {
mainClassName = "com.malinskiy.marathon.cli.ApplicationViewKt"
applicationName = "marathon"
applicationDefaultJvmArgs = listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044")
applicationDefaultJvmArgs = when(debugCoroutines) {
true -> listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044", "-Dkotlinx.coroutines.debug=on")
else -> listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044")
}
}

distributions {
Expand All @@ -25,7 +30,10 @@ distributions {
}
}

kotlin.experimental.coroutines = Coroutines.ENABLE
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.apiVersion = "1.3"
}

dependencies {
implementation(project(":core"))
Expand All @@ -34,6 +42,7 @@ dependencies {
implementation(Libraries.kotlinStdLib)
implementation(Libraries.kotlinCoroutines)
implementation(Libraries.kotlinLogging)
implementation(Libraries.kotlinReflect)
implementation(Libraries.slf4jAPI)
implementation(Libraries.logbackClassic)
implementation(Libraries.argParser)
Expand All @@ -43,19 +52,13 @@ dependencies {
implementation(Libraries.jacksonYaml)
implementation(Libraries.jacksonJSR310)
testCompile(TestLibraries.kluent)
testCompile(TestLibraries.mockitoKotlin)
testCompile(TestLibraries.spekAPI)
testRuntime(TestLibraries.spekJUnitPlatformEngine)
}

Deployment.initialize(project)

val compileKotlin by tasks.getting(KotlinCompile::class) {
kotlinOptions.jvmTarget = "1.8"
}
val compileTestKotlin by tasks.getting(KotlinCompile::class) {
kotlinOptions.jvmTarget = "1.8"
}

buildConfig {
appName = project.name
version = Versions.marathon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ 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.SuccessRateSortingStrategy
import com.malinskiy.marathon.ios.IOSConfiguration
import com.nhaarman.mockito_kotlin.whenever
import com.nhaarman.mockitokotlin2.whenever
import org.amshove.kluent.`it returns`
import org.amshove.kluent.`should be instance of`
import org.amshove.kluent.mock
Expand Down
5 changes: 3 additions & 2 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ plugins {
id("org.junit.platform.gradle.plugin")
}

kotlin.experimental.coroutines = Coroutines.ENABLE

sourceSets {
create("integrationTest") {
compileClasspath += sourceSets["main"].output
Expand Down Expand Up @@ -43,12 +41,14 @@ dependencies {
implementation(Libraries.slf4jAPI)
implementation(Libraries.logbackClassic)
implementation(Libraries.influxDbClient)
testCompile(project(":vendor-test"))
testCompile(TestLibraries.kluent)
testCompile(TestLibraries.spekAPI)
testRuntime(TestLibraries.spekJUnitPlatformEngine)
testRuntime(TestLibraries.jupiterEngine)
testCompile(TestLibraries.testContainers)
testCompile(TestLibraries.testContainersInflux)
testImplementation(TestLibraries.mockitoKotlin)
}


Expand All @@ -73,6 +73,7 @@ Deployment.initialize(project)

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.apiVersion = "1.3"
}

junitPlatform {
Expand Down
14 changes: 10 additions & 4 deletions core/src/main/kotlin/com/malinskiy/marathon/Marathon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import com.malinskiy.marathon.report.internal.TestResultReporter
import com.malinskiy.marathon.test.Test
import com.malinskiy.marathon.test.toTestName
import com.malinskiy.marathon.vendor.VendorConfiguration
import kotlinx.coroutines.experimental.runBlocking
import kotlinx.coroutines.runBlocking
import java.util.ServiceLoader
import java.util.concurrent.TimeUnit
import kotlin.coroutines.coroutineContext
import kotlin.system.measureTimeMillis

private val log = MarathonLogging.logger {}
Expand Down Expand Up @@ -68,7 +69,11 @@ class Marathon(val configuration: Configuration) {
return loader.first()
}

fun run(): Boolean = runBlocking {
fun run() = runBlocking {
runAsync()
}

suspend fun runAsync(): Boolean {
MarathonLogging.debug = configuration.debug

val testParser = loadTestParser(configuration.vendorConfiguration)
Expand All @@ -81,7 +86,8 @@ class Marathon(val configuration: Configuration) {
log.info("Scheduling ${tests.size} tests")
log.debug(tests.map { it.toTestName() }.joinToString(", "))
val progressReporter = ProgressReporter()
val scheduler = Scheduler(deviceProvider, analytics, configuration, tests, progressReporter)
val currentCoroutineContext = coroutineContext
val scheduler = Scheduler(deviceProvider, analytics, configuration, tests, progressReporter, currentCoroutineContext)

if (configuration.outputDir.exists()) {
log.info { "Output ${configuration.outputDir} already exists" }
Expand All @@ -108,7 +114,7 @@ class Marathon(val configuration: Configuration) {
analytics.terminate()
analytics.close()
deviceProvider.terminate()
progressReporter.aggregateResult()
return progressReporter.aggregateResult()
}

private fun applyTestFilters(parsedTests: List<Test>): List<Test> {
Expand Down
26 changes: 18 additions & 8 deletions core/src/main/kotlin/com/malinskiy/marathon/actor/Actor.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package com.malinskiy.marathon.actor

import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.channels.SendChannel
import kotlinx.coroutines.experimental.channels.actor
import kotlinx.coroutines.experimental.selects.SelectClause2
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.selects.SelectClause2
import kotlin.coroutines.CoroutineContext

abstract class Actor<in T>(parent: Job? = null) : SendChannel<T> {
abstract class Actor<in T>(parent: Job? = null,
val context: CoroutineContext) : SendChannel<T>, CoroutineScope {

protected abstract suspend fun receive(msg: T)
override val coroutineContext: CoroutineContext
get() = context + actorJob

private val actorJob = Job(parent)

private val delegate = actor<T>(
capacity = Channel.UNLIMITED,
parent = parent
context = coroutineContext
) {
for (msg in channel) {
receive(msg)
Expand All @@ -30,7 +37,10 @@ abstract class Actor<in T>(parent: Job? = null) : SendChannel<T> {
delegate.invokeOnClose(handler)
}

override fun close(cause: Throwable?): Boolean = delegate.close(cause)
override fun close(cause: Throwable?): Boolean {
actorJob.cancel()
return true
}

override fun offer(element: T): Boolean = delegate.offer(element)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.malinskiy.marathon.actor

import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel

fun <T> unboundedChannel() = Channel<T>(Channel.UNLIMITED)

suspend fun <T> SendChannel<T>.safeSend(element: T) {
if(isClosedForSend) return
send(element)
}
6 changes: 3 additions & 3 deletions core/src/main/kotlin/com/malinskiy/marathon/device/Device.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.malinskiy.marathon.execution.Configuration
import com.malinskiy.marathon.execution.TestBatchResults
import com.malinskiy.marathon.execution.progress.ProgressReporter
import com.malinskiy.marathon.test.TestBatch
import kotlinx.coroutines.experimental.CompletableDeferred
import kotlinx.coroutines.CompletableDeferred

interface Device {
val operatingSystem: OperatingSystem
Expand All @@ -16,13 +16,13 @@ interface Device {
val healthy: Boolean
val abi: String

fun execute(configuration: Configuration,
suspend fun execute(configuration: Configuration,
devicePoolId: DevicePoolId,
testBatch: TestBatch,
deferred: CompletableDeferred<TestBatchResults>,
progressReporter: ProgressReporter)

fun prepare(configuration: Configuration)
suspend fun prepare(configuration: Configuration)
fun dispose()
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.malinskiy.marathon.device

import com.malinskiy.marathon.vendor.VendorConfiguration
import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.channels.Channel

interface DeviceProvider {
sealed class DeviceEvent {
Expand All @@ -11,7 +11,5 @@ interface DeviceProvider {

fun initialize(vendorConfiguration: VendorConfiguration)
fun subscribe() : Channel<DeviceEvent>
fun lockDevice(device: Device) : Boolean
fun unlockDevice(device: Device) : Boolean
fun terminate()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.malinskiy.marathon.exceptions

/**
* Indicates that the execution device is no longer available
*/
class DeviceLostException: RuntimeException {
constructor(cause: Throwable): super(cause)
constructor(message: String): super(message)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.malinskiy.marathon.execution

import com.malinskiy.marathon.actor.Actor
import com.malinskiy.marathon.actor.safeSend
import com.malinskiy.marathon.analytics.Analytics
import com.malinskiy.marathon.device.Device
import com.malinskiy.marathon.device.DevicePoolId
Expand All @@ -12,15 +13,18 @@ import com.malinskiy.marathon.execution.queue.QueueMessage
import com.malinskiy.marathon.log.MarathonLogging
import com.malinskiy.marathon.test.Test
import com.malinskiy.marathon.test.TestBatch
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.channels.SendChannel
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.SendChannel
import kotlin.coroutines.CoroutineContext

class DevicePoolActor(private val poolId: DevicePoolId,
private val configuration: Configuration,
analytics: Analytics,
tests: Collection<Test>,
private val progressReporter: ProgressReporter,
parent: Job) : Actor<DevicePoolMessage>(parent = parent) {
parent: Job,
context: CoroutineContext) :
Actor<DevicePoolMessage>(parent = parent, context = context) {

private val logger = MarathonLogging.logger("DevicePoolActor[${poolId.name}]")

Expand All @@ -44,24 +48,20 @@ class DevicePoolActor(private val poolId: DevicePoolId,
private val flakinessShard = configuration.flakinessStrategy
private val shard = flakinessShard.process(shardingStrategy.createShard(tests), analytics)

private val queue: QueueActor = QueueActor(configuration, shard, analytics, this, poolId, progressReporter, poolJob)
private val queue: QueueActor = QueueActor(configuration, shard, analytics, this, poolId, progressReporter, poolJob, context)

private val devices = mutableMapOf<String, SendChannel<DeviceEvent>>()

private suspend fun notifyDevices() {
logger.debug { "Notify devices" }
devices.filter {
!it.value.isClosedForSend
}.forEach {
it.value.send(DeviceEvent.WakeUp)
devices.values.forEach {
it.safeSend(DeviceEvent.WakeUp)
}
}

private suspend fun onQueueTerminated() {
devices.filterValues {
!it.isClosedForSend
}.forEach {
it.value.send(DeviceEvent.Terminate)
devices.values.forEach {
it.safeSend(DeviceEvent.Terminate)
}
terminate()
}
Expand All @@ -80,9 +80,7 @@ class DevicePoolActor(private val poolId: DevicePoolId,

private suspend fun executeBatch(device: Device, batch: TestBatch) {
devices[device.serialNumber]?.run {
if (!isClosedForSend) {
send(DeviceEvent.Execute(batch))
}
safeSend(DeviceEvent.Execute(batch))
}
}

Expand All @@ -94,7 +92,7 @@ class DevicePoolActor(private val poolId: DevicePoolId,
private suspend fun removeDevice(device: Device) {
logger.debug { "remove device ${device.serialNumber}" }
val actor = devices.remove(device.serialNumber)
actor?.send(DeviceEvent.Terminate)
actor?.safeSend(DeviceEvent.Terminate)
logger.debug { "devices.size = ${devices.size}" }
if (noActiveDevices()) {
//TODO check if we still have tests and timeout if nothing available
Expand All @@ -111,8 +109,8 @@ class DevicePoolActor(private val poolId: DevicePoolId,
}

logger.debug { "add device ${device.serialNumber}" }
val actor = DeviceActor(poolId, this, configuration, device, progressReporter, poolJob)
val actor = DeviceActor(poolId, this, configuration, device, progressReporter, poolJob, coroutineContext)
devices[device.serialNumber] = actor
actor.send(DeviceEvent.Initialize)
actor.safeSend(DeviceEvent.Initialize)
}
}
Loading

0 comments on commit decd74f

Please sign in to comment.