@@ -53,17 +53,16 @@ import com.malinskiy.marathon.test.TestBatch
53
53
import com.malinskiy.marathon.time.Timer
54
54
import kotlinx.coroutines.CompletableDeferred
55
55
import kotlinx.coroutines.CoroutineScope
56
- import kotlinx.coroutines.DelicateCoroutinesApi
56
+ import kotlinx.coroutines.Dispatchers
57
57
import kotlinx.coroutines.Job
58
+ import kotlinx.coroutines.SupervisorJob
58
59
import kotlinx.coroutines.async
59
- import kotlinx.coroutines.newFixedThreadPoolContext
60
- import kotlinx.coroutines.runBlocking
60
+ import kotlinx.coroutines.cancel
61
61
import java.awt.image.BufferedImage
62
62
import java.io.File
63
63
import java.io.IOException
64
64
import java.util.UUID
65
65
import java.util.concurrent.TimeUnit
66
- import kotlin.coroutines.CoroutineContext
67
66
68
67
class DdmlibAndroidDevice (
69
68
val ddmsDevice : IDevice ,
@@ -75,15 +74,18 @@ class DdmlibAndroidDevice(
75
74
private val reportsFileManager : FileManager ,
76
75
private val serialStrategy : SerialStrategy ,
77
76
private val logcatListener : LogcatListener ,
78
- private val strictRunChecker : StrictRunChecker
79
- ) : Device, CoroutineScope, AndroidDevice {
77
+ private val strictRunChecker : StrictRunChecker ,
78
+ parentJob : Job = Job (),
79
+ ) : Device, AndroidDevice {
80
80
override val fileManager = RemoteFileManager (this )
81
81
82
82
override val version: AndroidVersion by lazy { ddmsDevice.version }
83
- private val nullOutputReceiver = NullOutputReceiver ()
84
- private val parentJob: Job = Job ()
85
83
86
- private var logcatReceiver: CliLogcatReceiver ? = null
84
+ private val dispatcher = Dispatchers .IO .limitedParallelism(1 )
85
+ private val job = SupervisorJob (parentJob)
86
+ private val coroutineScope = CoroutineScope (job + dispatcher)
87
+ private val logger = MarathonLogging .logger(DdmlibAndroidDevice ::class .java.simpleName)
88
+
87
89
private val logMessagesListener: (List <LogCatMessage >) -> Unit = {
88
90
it.forEach { msg ->
89
91
logcatListener.onMessage(this , msg.toMarathonLogcatMessage())
@@ -102,7 +104,7 @@ class DdmlibAndroidDevice(
102
104
103
105
override fun executeCommand (command : String , errorMessage : String ) {
104
106
try {
105
- ddmsDevice.safeExecuteShellCommand(command, nullOutputReceiver )
107
+ ddmsDevice.safeExecuteShellCommand(command, NullOutputReceiver () )
106
108
} catch (e: TimeoutException ) {
107
109
logger.error(" $errorMessage while executing $command " , e)
108
110
} catch (e: AdbCommandRejectedException ) {
@@ -147,14 +149,6 @@ class DdmlibAndroidDevice(
147
149
)
148
150
}
149
151
150
- override fun waitForAsyncWork () {
151
- runBlocking(context = coroutineContext) {
152
- parentJob.children.forEach {
153
- it.join()
154
- }
155
- }
156
- }
157
-
158
152
private fun bufferedImageFrom (rawImage : RawImage ): BufferedImage {
159
153
val image = BufferedImage (rawImage.width, rawImage.height, BufferedImage .TYPE_INT_ARGB )
160
154
@@ -169,15 +163,6 @@ class DdmlibAndroidDevice(
169
163
return image
170
164
}
171
165
172
- @OptIn(DelicateCoroutinesApi ::class )
173
- private val dispatcher by lazy {
174
- newFixedThreadPoolContext(1 , " AndroidDevice - execution - ${ddmsDevice.serialNumber} " )
175
- }
176
-
177
- override val coroutineContext: CoroutineContext = dispatcher
178
-
179
- private val logger = MarathonLogging .logger(DdmlibAndroidDevice ::class .java.simpleName)
180
-
181
166
override val abi: String by lazy {
182
167
ddmsDevice.getProperty(" ro.product.cpu.abi" ) ? : " Unknown"
183
168
}
@@ -231,6 +216,7 @@ class DdmlibAndroidDevice(
231
216
? : serialNumber.takeIf { it.isNotEmpty() }
232
217
? : UUID .randomUUID().toString()
233
218
}
219
+
234
220
SerialStrategy .MARATHON_PROPERTY -> marathonSerialProp
235
221
SerialStrategy .BOOT_PROPERTY -> serialProp
236
222
SerialStrategy .HOSTNAME -> hostName
@@ -276,15 +262,15 @@ class DdmlibAndroidDevice(
276
262
val androidComponentInfo = testBatch.componentInfo as AndroidComponentInfo
277
263
278
264
try {
279
- async { ensureInstalled(androidComponentInfo) }.await()
265
+ coroutineScope. async { ensureInstalled(androidComponentInfo) }.await()
280
266
} catch (@Suppress(" TooGenericExceptionCaught" ) e: Throwable ) {
281
267
logger.error(e) { " Terminating device $serialNumber due to installation failures" }
282
268
throw DeviceLostException (e)
283
269
}
284
270
285
271
safePrintToLogcat(SERVICE_LOGS_TAG , " \" batch_started: {${testBatch.id} }\" " )
286
272
287
- val deferredResult = async {
273
+ val deferredResult = coroutineScope. async {
288
274
val listeners = createListeners(configuration, devicePoolId, testBatch, deferred, progressReporter)
289
275
val listener = DdmlibTestRunListener (testBatch.componentInfo, listeners)
290
276
AndroidDeviceTestRunner (this @DdmlibAndroidDevice).execute(configuration, testBatch, listener)
@@ -334,21 +320,16 @@ class DdmlibAndroidDevice(
334
320
335
321
override suspend fun prepare (configuration : Configuration ) {
336
322
track.trackDevicePreparing(this ) {
337
- val deferred = async {
323
+ val logcatReceiver = CliLogcatReceiver (adbPath, reportsFileManager, ddmsDevice, logMessagesListener)
324
+ val deferred = coroutineScope.async {
338
325
clearLogcat(ddmsDevice)
339
-
340
- logcatReceiver = CliLogcatReceiver (adbPath, reportsFileManager, ddmsDevice, logMessagesListener)
341
- logcatReceiver?.start()
326
+ logcatReceiver.start()
342
327
}
328
+ job.invokeOnCompletion { logcatReceiver.close() }
343
329
deferred.await()
344
330
}
345
331
}
346
332
347
- override fun dispose () {
348
- logcatReceiver?.dispose()
349
- dispatcher.close()
350
- }
351
-
352
333
private fun selectRecorderType (preferred : DeviceFeature ? , features : Collection <DeviceFeature >) = when {
353
334
features.contains(preferred) -> preferred
354
335
features.contains(DeviceFeature .VIDEO ) -> DeviceFeature .VIDEO
@@ -370,6 +351,10 @@ class DdmlibAndroidDevice(
370
351
return receiver.output()
371
352
}
372
353
354
+ override fun close () {
355
+ coroutineScope.cancel()
356
+ }
357
+
373
358
private fun prepareRecorderListener (
374
359
feature : DeviceFeature ,
375
360
attachmentProviders : MutableList <AttachmentProvider >
@@ -381,7 +366,7 @@ class DdmlibAndroidDevice(
381
366
}
382
367
383
368
DeviceFeature .SCREENSHOT -> {
384
- ScreenCapturerTestRunListener (attachmentManager, this )
369
+ ScreenCapturerTestRunListener (attachmentManager, this , coroutineScope )
385
370
.also { attachmentProviders.add(it) }
386
371
}
387
372
}
0 commit comments