Skip to content

Commit 5a6533a

Browse files
author
Sergey Chelombitko
committed
Use BuildService
1 parent 58cd84d commit 5a6533a

File tree

8 files changed

+99
-72
lines changed

8 files changed

+99
-72
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.malinskiy.marathon
2+
3+
import com.malinskiy.marathon.execution.ComponentInfo
4+
import com.malinskiy.marathon.worker.WorkerContext
5+
import com.malinskiy.marathon.worker.WorkerHandler
6+
import org.gradle.api.file.DirectoryProperty
7+
import org.gradle.api.provider.Property
8+
import org.gradle.api.services.BuildService
9+
import org.gradle.api.services.BuildServiceParameters
10+
11+
abstract class MarathonBuildService : BuildService<MarathonBuildService.Params>, WorkerHandler {
12+
private val lazyWorkerContext = lazy {
13+
val configuration = createCommonConfiguration(
14+
parameters.marathonConfig.get(),
15+
parameters.adbPath.get().asFile,
16+
parameters.outputDir.get().asFile
17+
)
18+
WorkerContext(configuration)
19+
}
20+
21+
override fun scheduleTests(componentInfo: ComponentInfo) {
22+
lazyWorkerContext.value.scheduleTests(componentInfo)
23+
}
24+
25+
override fun await() {
26+
if (lazyWorkerContext.isInitialized()) {
27+
lazyWorkerContext.value.await()
28+
}
29+
}
30+
31+
override fun close() {
32+
if (lazyWorkerContext.isInitialized()) {
33+
lazyWorkerContext.value.close()
34+
}
35+
}
36+
37+
interface Params : BuildServiceParameters {
38+
val adbPath: DirectoryProperty
39+
val outputDir: DirectoryProperty
40+
val marathonConfig: Property<MarathonExtension>
41+
}
42+
43+
companion object {
44+
const val NAME = "marathon"
45+
}
46+
}

marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonPlugin.kt

+13-10
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ import com.android.build.api.variant.GeneratesTestApk
88
import com.android.build.api.variant.TestVariant
99
import com.android.build.api.variant.Variant
1010
import com.malinskiy.marathon.android.findAdbPath
11-
import com.malinskiy.marathon.worker.MarathonWorker
1211
import org.gradle.api.Plugin
1312
import org.gradle.api.Project
1413
import org.gradle.api.plugins.JavaBasePlugin
1514
import org.gradle.api.tasks.TaskProvider
15+
import org.gradle.kotlin.dsl.create
16+
import org.gradle.kotlin.dsl.named
17+
import org.gradle.kotlin.dsl.register
18+
import org.gradle.kotlin.dsl.registerIfAbsent
1619

1720
class MarathonPlugin : Plugin<Project> {
1821
override fun apply(project: Project) {
@@ -31,16 +34,16 @@ class MarathonPlugin : Plugin<Project> {
3134
}
3235

3336
private fun Project.configureRootProject() {
34-
val marathonConfig = extensions.create(MarathonExtension.NAME, MarathonExtension::class.java)
37+
val marathonConfig = extensions.create<MarathonExtension>(MarathonExtension.NAME)
3538
marathonConfig.initDefaults()
3639

37-
tasks.register(WORKER_TASK_NAME, MarathonWorkerRunTask::class.java)
38-
39-
gradle.projectsEvaluated {
40-
val outputDir = layout.buildDirectory.dir("reports/marathon").get().asFile
41-
val configuration = createCommonConfiguration(marathonConfig, findAdbPath(projectDir), outputDir)
42-
MarathonWorker.initialize(configuration)
40+
gradle.sharedServices.registerIfAbsent(MarathonBuildService.NAME, MarathonBuildService::class) {
41+
parameters.adbPath.set(findAdbPath(projectDir))
42+
parameters.outputDir.set(layout.buildDirectory.dir("reports/marathon"))
43+
parameters.marathonConfig.set(marathonConfig)
4344
}
45+
46+
tasks.register<MarathonWorkerRunTask>(WORKER_TASK_NAME)
4447
}
4548

4649
private fun Project.configureAndroidProject() {
@@ -49,7 +52,7 @@ class MarathonPlugin : Plugin<Project> {
4952
description = "Runs all the instrumentation test variations on all the connected devices"
5053
}
5154

52-
val marathonWorkerTask = rootProject.tasks.named(WORKER_TASK_NAME, MarathonWorkerRunTask::class.java)
55+
val marathonWorkerTask = rootProject.tasks.named<MarathonWorkerRunTask>(WORKER_TASK_NAME)
5356
val androidComponents = extensions.getByType(AndroidComponentsExtension::class.java)
5457
androidComponents.onVariants { variant ->
5558
variant.components
@@ -66,7 +69,7 @@ class MarathonPlugin : Plugin<Project> {
6669
testComponent: Component,
6770
marathonWorkerTask: TaskProvider<MarathonWorkerRunTask>
6871
): TaskProvider<MarathonScheduleTestsToWorkerTask> =
69-
tasks.register(variant.computeTaskName(TASK_PREFIX, "androidTest"), MarathonScheduleTestsToWorkerTask::class.java) {
72+
tasks.register<MarathonScheduleTestsToWorkerTask>(variant.computeTaskName(TASK_PREFIX, "androidTest")) {
7073
group = JavaBasePlugin.VERIFICATION_GROUP
7174
description = "Runs instrumentation tests on all the connected devices for '${variant.name}' " +
7275
"variation and generates a report with screenshots"

marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/MarathonScheduleTestsToWorkerTask.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package com.malinskiy.marathon
33
import com.android.build.api.variant.BuiltArtifacts
44
import com.android.build.api.variant.BuiltArtifactsLoader
55
import com.malinskiy.marathon.android.AndroidComponentInfo
6-
import com.malinskiy.marathon.worker.MarathonWorker
76
import org.gradle.api.DefaultTask
87
import org.gradle.api.file.DirectoryProperty
98
import org.gradle.api.provider.Property
9+
import org.gradle.api.services.ServiceReference
1010
import org.gradle.api.tasks.Input
1111
import org.gradle.api.tasks.InputFiles
1212
import org.gradle.api.tasks.Internal
@@ -30,6 +30,9 @@ abstract class MarathonScheduleTestsToWorkerTask : DefaultTask() {
3030
@get:Internal
3131
abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader>
3232

33+
@get:ServiceReference(MarathonBuildService.NAME)
34+
abstract val buildService: Property<MarathonBuildService>
35+
3336
@TaskAction
3437
fun run() {
3538
val artifactsLoader = builtArtifactsLoader.get()
@@ -48,7 +51,7 @@ abstract class MarathonScheduleTestsToWorkerTask : DefaultTask() {
4851
(componentInfo.applicationOutput?.let { " for app $it" } ?: "")
4952
)
5053

51-
MarathonWorker.scheduleTests(componentInfo)
54+
buildService.get().scheduleTests(componentInfo)
5255
}
5356

5457
private val BuiltArtifacts.singleFile: File
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package com.malinskiy.marathon
22

3-
import com.malinskiy.marathon.worker.MarathonWorker
43
import org.gradle.api.DefaultTask
4+
import org.gradle.api.provider.Property
5+
import org.gradle.api.services.ServiceReference
56
import org.gradle.api.tasks.TaskAction
7+
import org.gradle.work.DisableCachingByDefault
68

9+
@DisableCachingByDefault
710
abstract class MarathonWorkerRunTask : DefaultTask() {
11+
@get:ServiceReference(MarathonBuildService.NAME)
12+
abstract val buildService: Property<MarathonBuildService>
13+
814
@TaskAction
915
fun run() {
10-
MarathonWorker.await()
16+
buildService.get().await()
1117
}
1218
}

marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/worker/MarathonWorker.kt

-22
This file was deleted.

marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/worker/WorkerContext.kt

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
package com.malinskiy.marathon.worker
22

3+
import com.malinskiy.marathon.Marathon
4+
import com.malinskiy.marathon.di.marathonStartKoin
35
import com.malinskiy.marathon.execution.ComponentInfo
46
import com.malinskiy.marathon.execution.Configuration
57
import kotlinx.coroutines.channels.Channel
68
import java.util.concurrent.CountDownLatch
7-
import java.util.concurrent.ExecutorService
89
import java.util.concurrent.Executors
910
import java.util.concurrent.Future
1011
import java.util.concurrent.TimeUnit
1112
import java.util.concurrent.atomic.AtomicBoolean
1213

13-
class WorkerContext : WorkerHandler {
14-
15-
private lateinit var configuration: Configuration
14+
internal class WorkerContext(configuration: Configuration) : WorkerHandler {
15+
private val executor = Executors.newSingleThreadExecutor()
1616
private val componentsChannel: Channel<ComponentInfo> = Channel(capacity = Channel.UNLIMITED)
1717

18+
private val application = marathonStartKoin(configuration)
19+
private val marathon = application.koin.get<Marathon>()
1820
private val isRunning = AtomicBoolean(false)
1921
private val startedLatch = CountDownLatch(1)
2022

21-
private lateinit var executor: ExecutorService
2223
private lateinit var finishFuture: Future<*>
2324

24-
override fun initialize(configuration: Configuration) {
25-
this.configuration = configuration
26-
}
27-
2825
override fun scheduleTests(componentInfo: ComponentInfo) {
2926
ensureStarted()
3027
componentsChannel.trySend(componentInfo)
@@ -44,12 +41,17 @@ class WorkerContext : WorkerHandler {
4441
}
4542
}
4643

44+
override fun close() {
45+
isRunning.set(false)
46+
componentsChannel.close()
47+
executor.shutdown()
48+
application.close()
49+
}
50+
4751
private fun ensureStarted() {
4852
if (isRunning.getAndSet(true)) return
4953

50-
val runnable = WorkerRunnable(componentsChannel, configuration)
51-
52-
executor = Executors.newSingleThreadExecutor()
54+
val runnable = WorkerRunnable(marathon, componentsChannel)
5355
finishFuture = executor.submit(runnable)
5456

5557
startedLatch.countDown()
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package com.malinskiy.marathon.worker
22

33
import com.malinskiy.marathon.execution.ComponentInfo
4-
import com.malinskiy.marathon.execution.Configuration
54

6-
interface WorkerHandler {
7-
fun initialize(configuration: Configuration)
5+
interface WorkerHandler : AutoCloseable {
86
fun scheduleTests(componentInfo: ComponentInfo)
97
fun await()
108
}
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,38 @@
11
package com.malinskiy.marathon.worker
22

33
import com.malinskiy.marathon.Marathon
4-
import com.malinskiy.marathon.di.marathonStartKoin
54
import com.malinskiy.marathon.execution.ComponentInfo
6-
import com.malinskiy.marathon.execution.Configuration
75
import com.malinskiy.marathon.log.MarathonLogging
86
import kotlinx.coroutines.channels.Channel
97
import kotlinx.coroutines.runBlocking
108
import org.gradle.api.GradleException
119

12-
class WorkerRunnable(
13-
private val componentsChannel: Channel<ComponentInfo>,
14-
private val configuration: Configuration
10+
internal class WorkerRunnable(
11+
private val marathon: Marathon,
12+
private val componentsChannel: Channel<ComponentInfo>
1513
) : Runnable {
1614

1715
private val log = MarathonLogging.logger {}
1816

1917
override fun run() = runBlocking {
2018
log.debug("Starting Marathon worker")
19+
marathon.start()
2120

22-
val application = marathonStartKoin(configuration)
23-
try {
24-
val marathon = application.koin.get<Marathon>()
25-
marathon.start()
26-
27-
for (component in componentsChannel) {
28-
log.debug("Scheduling tests for $component")
29-
marathon.scheduleTests(component)
30-
}
31-
32-
log.debug("Waiting for completion")
33-
stopAndWaitForCompletion(marathon)
34-
} finally {
35-
application.close()
21+
for (component in componentsChannel) {
22+
log.debug("Scheduling tests for $component")
23+
marathon.scheduleTests(component)
3624
}
25+
26+
log.debug("Waiting for completion")
27+
stopAndWaitForCompletion(marathon)
3728
}
3829

3930
private suspend fun stopAndWaitForCompletion(marathon: Marathon) {
4031
val success = marathon.stopAndWaitForCompletion()
4132

42-
val shouldReportFailure = !configuration.ignoreFailures
33+
val shouldReportFailure = !marathon.configuration.ignoreFailures
4334
if (!success && shouldReportFailure) {
44-
throw GradleException("Tests failed! See ${configuration.outputDir}/html/index.html")
35+
throw GradleException("Tests failed! See ${marathon.configuration.outputDir}/html/index.html")
4536
}
4637
}
4738
}

0 commit comments

Comments
 (0)