From 1f104034705e99206f68f2fe1fe6a5c0bd9263a0 Mon Sep 17 00:00:00 2001 From: Ruslan Mingaliev Date: Thu, 12 Aug 2021 11:45:16 +0300 Subject: [PATCH] MBS-11465 Runner execution timeout (#1165) --- .../extensions/ChannelExtensions.kt | 8 ++ .../InstrumentationConfigurationData.kt | 4 +- .../runner/scheduler/TestRunnerFactoryImpl.kt | 3 +- .../scheduler/report/model/SummaryReport.kt | 2 +- .../runner/scheduler/runner/TestRunnerImpl.kt | 121 ++++++++++-------- .../scheduler/runner/RunnerIntegrationTest.kt | 89 ++++++++++--- .../StubInstrumentationConfigurationData.kt | 7 +- .../runner/devices/StubDevicesProvider.kt | 5 +- .../InstrumentationTestsPlugin.kt | 3 +- .../InstrumentationConfiguration.kt | 9 +- .../runner/service/worker/DeviceWorker.kt | 2 +- .../runner/service/worker/device/Device.kt | 2 +- .../service/worker/device/adb/AdbDevice.kt | 2 +- .../service/worker/device/stub/StubDevice.kt | 29 +++-- 14 files changed, 188 insertions(+), 98 deletions(-) create mode 100644 subprojects/common/coroutines-extension/src/main/kotlin/com/avito/coroutines/extensions/ChannelExtensions.kt diff --git a/subprojects/common/coroutines-extension/src/main/kotlin/com/avito/coroutines/extensions/ChannelExtensions.kt b/subprojects/common/coroutines-extension/src/main/kotlin/com/avito/coroutines/extensions/ChannelExtensions.kt new file mode 100644 index 0000000000..7cfa0fc737 --- /dev/null +++ b/subprojects/common/coroutines-extension/src/main/kotlin/com/avito/coroutines/extensions/ChannelExtensions.kt @@ -0,0 +1,8 @@ +package com.avito.coroutines.extensions + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.Channel + +@ExperimentalCoroutinesApi +public val Channel.isClosedForSendAndReceive: Boolean + get() = this.isClosedForSend && this.isClosedForReceive diff --git a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/config/InstrumentationConfigurationData.kt b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/config/InstrumentationConfigurationData.kt index 26ccde86b1..9b5f687fe9 100644 --- a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/config/InstrumentationConfigurationData.kt +++ b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/config/InstrumentationConfigurationData.kt @@ -4,6 +4,7 @@ import com.avito.android.runner.devices.model.DeviceType import com.avito.instrumentation.reservation.request.Device import java.io.File import java.io.Serializable +import java.time.Duration public data class InstrumentationConfigurationData( val name: String, @@ -12,7 +13,8 @@ public data class InstrumentationConfigurationData( val kubernetesNamespace: String, val targets: List, val enableDeviceDebug: Boolean, - val timeoutInSeconds: Long, + val testRunnerExecutionTimeout: Duration, + val instrumentationTaskTimeout: Duration, val filter: InstrumentationFilterData, val outputFolder: File, ) : Serializable { diff --git a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/TestRunnerFactoryImpl.kt b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/TestRunnerFactoryImpl.kt index 99a294ad20..044f26910b 100644 --- a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/TestRunnerFactoryImpl.kt +++ b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/TestRunnerFactoryImpl.kt @@ -80,7 +80,8 @@ internal class TestRunnerFactoryImpl( ), testSuiteListener = testSuiteListener, testRunRequestFactory = testRunnerRequestFactory, - targets = targets + targets = targets, + executionTimeout = params.instrumentationConfiguration.testRunnerExecutionTimeout ) } diff --git a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/report/model/SummaryReport.kt b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/report/model/SummaryReport.kt index e136538420..fbf24bc306 100644 --- a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/report/model/SummaryReport.kt +++ b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/report/model/SummaryReport.kt @@ -11,7 +11,7 @@ internal class SummaryReport( reports.count { it.result is TestCaseRequestMatchingReport.Result.Matched } } - val mismatched: Int by lazy { + val mismatchedCount: Int by lazy { reports.count { it.result is TestCaseRequestMatchingReport.Result.Mismatched } } diff --git a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/runner/TestRunnerImpl.kt b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/runner/TestRunnerImpl.kt index b991084cd0..148ff99ac5 100644 --- a/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/runner/TestRunnerImpl.kt +++ b/subprojects/test-runner/client/src/main/kotlin/com/avito/runner/scheduler/runner/TestRunnerImpl.kt @@ -18,7 +18,10 @@ import com.avito.runner.service.DeviceWorkerPool import com.avito.test.model.DeviceName import com.avito.test.model.TestCase import com.avito.time.millisecondsToHumanReadableTime +import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.withTimeout +import java.time.Duration internal class TestRunnerImpl( private val scheduler: TestExecutionScheduler, @@ -30,77 +33,83 @@ internal class TestRunnerImpl( private val devicesProvider: DevicesProvider, private val testRunRequestFactory: TestRunRequestFactory, private val targets: List, + private val executionTimeout: Duration, loggerFactory: LoggerFactory ) : TestRunner { private val logger = loggerFactory.create() override suspend fun runTests(tests: List): Result { - return coroutineScope { - val startTime = System.currentTimeMillis() - testSuiteListener.onTestSuiteStarted() - logger.info("Test run started") - val deviceWorkerPool: DeviceWorkerPool = devicesProvider.provideFor( - reservations = getReservations(tests), - ) - try { - deviceWorkerPool.start() - reservationWatcher.watch(state.deviceSignals) - scheduler.start( - requests = getTestRequests(tests), + return withTimeout(executionTimeout.toMillis()) { + coroutineScope { + val startTime = System.currentTimeMillis() + testSuiteListener.onTestSuiteStarted() + logger.info("Test run started") + val deviceWorkerPool: DeviceWorkerPool = devicesProvider.provideFor( + reservations = getReservations(tests), ) + try { + deviceWorkerPool.start() + reservationWatcher.watch(state.deviceSignals) + scheduler.start( + requests = getTestRequests(tests), + ) - val expectedResultsCount = tests.count() + val expectedResultsCount = tests.count() - val gottenResults = mutableListOf() - for (result in state.results) { - gottenResults.add(result) - val gottenCount = gottenResults.size + val gottenResults = mutableListOf() + for (result in state.results) { + gottenResults.add(result) + val gottenCount = gottenResults.size - logger.debug( - "Result for test: %s received after %d tries. Progress (%s)".format( - result.request.testCase.name, - result.result.size, - "$gottenCount/$expectedResultsCount" + logger.debug( + "Result for test: %s received after %d tries. Progress (%s)".format( + result.request.testCase.name, + result.result.size, + "$gottenCount/$expectedResultsCount" + ) ) - ) - if (gottenCount == expectedResultsCount) { - break + if (gottenCount == expectedResultsCount) { + break + } } - } - val result = TestRunnerResult( - runs = gottenResults.associate { - it.request.testCase to it.result - } - ) + val result = TestRunnerResult( + runs = gottenResults.associate { + it.request.testCase to it.result + } + ) - val summaryReport = summaryReportMaker.make(gottenResults, startTime) - reporter.report(report = summaryReport) - logger.debug( - "Test run finished. The results: " + - "passed = ${summaryReport.successRunsCount}, " + - "failed = ${summaryReport.failedRunsCount}, " + - "ignored = ${summaryReport.ignoredRunsCount}, " + - "took ${summaryReport.durationMilliseconds.millisecondsToHumanReadableTime()}." - ) - logger.debug( - "Matching results: " + - "matched = ${summaryReport.matchedCount}, " + - "mismatched = ${summaryReport.mismatched}, " + - "ignored = ${summaryReport.ignoredCount}." - ) + val summaryReport = summaryReportMaker.make(gottenResults, startTime) + reporter.report(report = summaryReport) + logger.debug( + "Test run finished. The results: " + + "passed = ${summaryReport.successRunsCount}, " + + "failed = ${summaryReport.failedRunsCount}, " + + "ignored = ${summaryReport.ignoredRunsCount}, " + + "took ${summaryReport.durationMilliseconds.millisecondsToHumanReadableTime()}." + ) + logger.debug( + "Matching results: " + + "matched = ${summaryReport.matchedCount}, " + + "mismatched = ${summaryReport.mismatchedCount}, " + + "ignored = ${summaryReport.ignoredCount}." + ) - testSuiteListener.onTestSuiteFinished() - logger.info("Test run end successfully") - Result.Success(result) - } catch (e: Throwable) { - logger.critical("Test run end with error", e) - Result.Failure(e) - } finally { - deviceWorkerPool.stop() - state.cancel() - devicesProvider.releaseDevices() + testSuiteListener.onTestSuiteFinished() + logger.info("Test run end successfully") + Result.Success(result) + } catch (e: TimeoutCancellationException) { + logger.critical("Test run end with timeout", e) + Result.Failure(e) + } catch (e: Throwable) { + logger.critical("Test run end with error", e) + Result.Failure(e) + } finally { + deviceWorkerPool.stop() + state.cancel() + devicesProvider.releaseDevices() + } } } } diff --git a/subprojects/test-runner/client/src/test/kotlin/com/avito/runner/scheduler/runner/RunnerIntegrationTest.kt b/subprojects/test-runner/client/src/test/kotlin/com/avito/runner/scheduler/runner/RunnerIntegrationTest.kt index bd9bbc8330..764ccee255 100644 --- a/subprojects/test-runner/client/src/test/kotlin/com/avito/runner/scheduler/runner/RunnerIntegrationTest.kt +++ b/subprojects/test-runner/client/src/test/kotlin/com/avito/runner/scheduler/runner/RunnerIntegrationTest.kt @@ -1,9 +1,12 @@ package com.avito.runner.scheduler.runner import com.avito.android.Result +import com.avito.android.runner.devices.DevicesProvider import com.avito.android.runner.devices.StubDevicesProvider import com.avito.coroutines.extensions.Dispatchers +import com.avito.coroutines.extensions.isClosedForSendAndReceive import com.avito.logger.StubLoggerFactory +import com.avito.runner.config.InstrumentationConfigurationData import com.avito.runner.config.QuotaConfigurationData import com.avito.runner.config.TargetConfigurationData import com.avito.runner.config.createStubInstance @@ -36,6 +39,7 @@ import com.avito.test.model.TestName import com.avito.time.StubTimeProvider import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.delay @@ -47,6 +51,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.io.TempDir import java.io.File +import java.time.Duration import java.util.concurrent.TimeUnit @ExperimentalCoroutinesApi @@ -546,6 +551,37 @@ internal class RunnerIntegrationTest { .isEqualTo("devices channel was closed") } + @Test + fun `tests execution timeout - run failed`() = runBlockingTest { + val targets = listOf(createTarget()) + val devicesProvider = createDevicesProvider() + val tests = createTests(2) + val runner = provideRunner( + targets = targets, + devicesProvider = devicesProvider, + executionTimeout = Duration.ofMinutes(15) + ) + val device = StubDevice( + loggerFactory = loggerFactory, + model = deviceModel, + apiResult = StubActionResult.Success(deviceApi), + coordinate = DeviceCoordinate.Local.createStubInstance(), + installApplicationResults = List(2) { installApplicationSuccess() }, // main and test apps + gettingDeviceStatusResults = List(3) { DeviceStatus.Alive }, // initial and a try for each device + clearPackageResults = List(4) { succeedClearPackage() }, // main and test apps for each test + runTestsResults = List(2) { testPassed() }, + testExecutionTime = Duration.ofMinutes(10) + ) + + devices.send(device) + assertThrows { + runner.runTests(tests) + } + + assertThat(devicesProvider.isReleased).isTrue() + state.assertIsCancelled() + } + private fun createTarget(quota: QuotaConfigurationData = defaultQuota) = TargetConfigurationData.createStubInstance( api = deviceApi, @@ -622,7 +658,7 @@ internal class RunnerIntegrationTest { ) } - private fun createSuccessfulDevice(testsCount: Int): StubDevice { + private fun createSuccessfulDevice(testsCount: Int, testExecutionTime: Duration = Duration.ZERO): StubDevice { return StubDevice( tag = "StubDevice:normal", model = deviceModel, @@ -642,7 +678,8 @@ internal class RunnerIntegrationTest { gettingDeviceStatusResults = List(testsCount + 1) { DeviceStatus.Alive }, runTestsResults = (0 until testsCount).map { testPassed() - } + }, + testExecutionTime = testExecutionTime ) } @@ -654,8 +691,27 @@ internal class RunnerIntegrationTest { private fun succeedClearPackage() = StubActionResult.Success>(Result.Success(Unit)) + private fun createDevicesProvider() = StubDevicesProvider( + provider = DeviceWorkerPoolProvider( + timeProvider = StubTimeProvider(), + loggerFactory = loggerFactory, + deviceListener = StubDeviceListener(), + intentions = state.intentions, + intentionResults = state.intentionResults, + deviceSignals = state.deviceSignals, + dispatchers = object : Dispatchers { + override fun dispatcher() = testCoroutineDispatcher + }, + testRunnerOutputDir = outputDirectory, + testListener = NoOpTestListener + ), + devices = devices + ) + private fun provideRunner( - targets: List + targets: List, + devicesProvider: DevicesProvider = createDevicesProvider(), + executionTimeout: Duration = InstrumentationConfigurationData.createStubInstance().testRunnerExecutionTimeout ): TestRunner { val testRunRequestFactory = TestRunRequestFactory( application = File("stub"), @@ -682,24 +738,10 @@ internal class RunnerIntegrationTest { summaryReportMaker = SummaryReportMakerImpl(), reporter = CompositeReporter(emptyList()), testSuiteListener = StubTestMetricsListener, - devicesProvider = StubDevicesProvider( - provider = DeviceWorkerPoolProvider( - timeProvider = StubTimeProvider(), - loggerFactory = loggerFactory, - deviceListener = StubDeviceListener(), - intentions = state.intentions, - intentionResults = state.intentionResults, - deviceSignals = state.deviceSignals, - dispatchers = object : Dispatchers { - override fun dispatcher() = testCoroutineDispatcher - }, - testRunnerOutputDir = outputDirectory, - testListener = NoOpTestListener - ), - devices = devices - ), + devicesProvider = devicesProvider, testRunRequestFactory = testRunRequestFactory, - targets = targets + targets = targets, + executionTimeout = executionTimeout ) } @@ -717,6 +759,13 @@ internal class RunnerIntegrationTest { device = device.getData() ) + private fun TestRunnerExecutionState.assertIsCancelled() { + assertThat(results.isClosedForSendAndReceive).isTrue() + assertThat(intentions.isClosedForSendAndReceive).isTrue() + assertThat(intentionResults.isClosedForSendAndReceive).isTrue() + assertThat(deviceSignals.isClosedForSendAndReceive).isTrue() + } + private fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) { testCoroutineDispatcher.runBlockingTest(block) } diff --git a/subprojects/test-runner/client/src/testFixtures/kotlin/com/avito/runner/config/StubInstrumentationConfigurationData.kt b/subprojects/test-runner/client/src/testFixtures/kotlin/com/avito/runner/config/StubInstrumentationConfigurationData.kt index 71b51c41e9..27c325bbd2 100644 --- a/subprojects/test-runner/client/src/testFixtures/kotlin/com/avito/runner/config/StubInstrumentationConfigurationData.kt +++ b/subprojects/test-runner/client/src/testFixtures/kotlin/com/avito/runner/config/StubInstrumentationConfigurationData.kt @@ -2,6 +2,7 @@ package com.avito.runner.config import com.avito.runner.scheduler.suite.filter.Filter import java.io.File +import java.time.Duration public fun InstrumentationConfigurationData.Companion.createStubInstance( name: String = "name", @@ -10,7 +11,8 @@ public fun InstrumentationConfigurationData.Companion.createStubInstance( kubernetesNamespace: String = "kubernetesNamespace", targets: List = emptyList(), enableDeviceDebug: Boolean = false, - timeoutInSecond: Long = 100, + testRunnerExecutionTimeout: Duration = Duration.ofSeconds(100), + instrumentationTaskTimeout: Duration = Duration.ofSeconds(120), previousRunExcluded: Set = emptySet(), outputFolder: File = File("") ): InstrumentationConfigurationData = InstrumentationConfigurationData( @@ -20,7 +22,8 @@ public fun InstrumentationConfigurationData.Companion.createStubInstance( kubernetesNamespace = kubernetesNamespace, targets = targets, enableDeviceDebug = enableDeviceDebug, - timeoutInSeconds = timeoutInSecond, + testRunnerExecutionTimeout = testRunnerExecutionTimeout, + instrumentationTaskTimeout = instrumentationTaskTimeout, filter = InstrumentationFilterData( name = "stub", fromSource = InstrumentationFilterData.FromSource( diff --git a/subprojects/test-runner/device-provider/impl/src/testFixtures/kotlin/com/avito/android/runner/devices/StubDevicesProvider.kt b/subprojects/test-runner/device-provider/impl/src/testFixtures/kotlin/com/avito/android/runner/devices/StubDevicesProvider.kt index 597b23a089..26aa8df188 100644 --- a/subprojects/test-runner/device-provider/impl/src/testFixtures/kotlin/com/avito/android/runner/devices/StubDevicesProvider.kt +++ b/subprojects/test-runner/device-provider/impl/src/testFixtures/kotlin/com/avito/android/runner/devices/StubDevicesProvider.kt @@ -12,12 +12,15 @@ public class StubDevicesProvider( private val devices: ReceiveChannel ) : DevicesProvider { + public var isReleased: Boolean = false + private set + override suspend fun provideFor( reservations: Collection, ): DeviceWorkerPool = provider.provide(devices) override suspend fun releaseDevices() { - // do nothing + isReleased = true } override suspend fun releaseDevice(coordinate: DeviceCoordinate) { diff --git a/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/InstrumentationTestsPlugin.kt b/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/InstrumentationTestsPlugin.kt index 0f74ee4acb..53e5d52551 100644 --- a/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/InstrumentationTestsPlugin.kt +++ b/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/InstrumentationTestsPlugin.kt @@ -284,7 +284,8 @@ public class InstrumentationTestsPlugin : Plugin { kubernetesNamespace = configuration.kubernetesNamespace, targets = getTargets(configuration, mergedInstrumentationParameters), enableDeviceDebug = configuration.enableDeviceDebug, - timeoutInSeconds = configuration.timeoutInSeconds, + testRunnerExecutionTimeout = configuration.testRunnerExecutionTimeout, + instrumentationTaskTimeout = configuration.instrumentationTaskTimeout, filter = filters.singleOrNull { it.name == configuration.filter } ?: throw IllegalStateException("Can't find filter=${configuration.filter}"), outputFolder = outputFolder diff --git a/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/configuration/InstrumentationConfiguration.kt b/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/configuration/InstrumentationConfiguration.kt index cfd5e09fb3..6de30f70cb 100644 --- a/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/configuration/InstrumentationConfiguration.kt +++ b/subprojects/test-runner/instrumentation-tests/src/main/kotlin/com/avito/instrumentation/configuration/InstrumentationConfiguration.kt @@ -3,7 +3,7 @@ package com.avito.instrumentation.configuration import com.avito.instrumentation.configuration.target.TargetConfiguration import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer -import java.util.concurrent.TimeUnit +import java.time.Duration public abstract class InstrumentationConfiguration(public val name: String) { @@ -17,7 +17,12 @@ public abstract class InstrumentationConfiguration(public val name: String) { public var kubernetesNamespace: String = "default" - public var timeoutInSeconds: Long = TimeUnit.MINUTES.toSeconds(100) + @Deprecated("Use testRunnerExecutionTimeout and instrumentationTaskTimeout properties instead") + public var timeoutInSeconds: Long = 120L // TODO: remove after MBS-11465 + + public var testRunnerExecutionTimeout: Duration = Duration.ofMinutes(100) + + public var instrumentationTaskTimeout: Duration = Duration.ofMinutes(120) public var enableDeviceDebug: Boolean = false diff --git a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/DeviceWorker.kt b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/DeviceWorker.kt index b857c7885d..8808d1ae90 100644 --- a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/DeviceWorker.kt +++ b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/DeviceWorker.kt @@ -129,7 +129,7 @@ internal class DeviceWorker( stateWorker.clearPackages(currentState) }.map { intendedState } - private fun executeAction(action: InstrumentationTestRunAction): Result { + private suspend fun executeAction(action: InstrumentationTestRunAction): Result { return Result.tryCatch { try { diff --git a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/Device.kt b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/Device.kt index b6b11af190..eb6e50366e 100644 --- a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/Device.kt +++ b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/Device.kt @@ -27,7 +27,7 @@ public interface Device { public fun installApplication(applicationPackage: String): Result - public fun runIsolatedTest( + public suspend fun runIsolatedTest( action: InstrumentationTestRunAction, outputDir: File ): DeviceTestCaseRun diff --git a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/adb/AdbDevice.kt b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/adb/AdbDevice.kt index 1a1b716836..4d1dcd5f65 100644 --- a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/adb/AdbDevice.kt +++ b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/adb/AdbDevice.kt @@ -99,7 +99,7 @@ public data class AdbDevice( } } - override fun runIsolatedTest( + override suspend fun runIsolatedTest( action: InstrumentationTestRunAction, outputDir: File ): DeviceTestCaseRun { diff --git a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/stub/StubDevice.kt b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/stub/StubDevice.kt index ed81da8c63..dce8fbb2b5 100644 --- a/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/stub/StubDevice.kt +++ b/subprojects/test-runner/service/src/main/kotlin/com/avito/runner/service/worker/device/stub/StubDevice.kt @@ -18,8 +18,10 @@ import com.avito.runner.service.worker.device.model.getData import com.avito.runner.service.worker.model.DeviceInstallation import com.avito.runner.service.worker.model.Installation import com.google.gson.Gson +import kotlinx.coroutines.delay import java.io.File import java.nio.file.Path +import java.time.Duration import java.util.ArrayDeque import java.util.Date import java.util.Queue @@ -34,7 +36,8 @@ public open class StubDevice( clearPackageResults: List>> = emptyList(), private val apiResult: StubActionResult = StubActionResult.Success(22), override val online: Boolean = true, - override val model: String = "model" + override val model: String = "model", + private val testExecutionTime: Duration = Duration.ZERO ) : Device { private val gson = Gson() @@ -56,7 +59,7 @@ public open class StubDevice( override fun installApplication(applicationPackage: String): Result { resultQueuePrecondition( queue = installApplicationResultsQueue, - functionName = "installApplication", + functionName = FUNCTION_INSTALL_APPLICATION, values = applicationPackage ) @@ -67,13 +70,15 @@ public open class StubDevice( return result } - override fun runIsolatedTest( + override suspend fun runIsolatedTest( action: InstrumentationTestRunAction, outputDir: File ): DeviceTestCaseRun { + delay(testExecutionTime.toMillis()) + resultQueuePrecondition( queue = runTestsResultsQueue, - functionName = "runIsolatedTest", + functionName = FUNCTION_RUN_ISOLATED_TEST, values = action.toString() ) @@ -95,7 +100,7 @@ public open class StubDevice( override fun clearPackage(name: String): Result { resultQueuePrecondition( queue = clearPackageResultsQueue, - functionName = "clearPackage", + functionName = FUNCTION_CLEAR_PACKAGE, values = name ) @@ -149,7 +154,7 @@ public open class StubDevice( override fun deviceStatus(): Device.DeviceStatus { resultQueuePrecondition( queue = gettingDeviceStatusResultsQueue, - functionName = "deviceStatus", + functionName = FUNCTION_DEVICE_STATUS, values = "" ) @@ -172,10 +177,10 @@ public open class StubDevice( } public fun verify() { - verifyQueueHasNoExcessiveElements(installApplicationResultsQueue, "installApplication") - verifyQueueHasNoExcessiveElements(gettingDeviceStatusResultsQueue, "deviceStatus") - verifyQueueHasNoExcessiveElements(runTestsResultsQueue, "runIsolatedTest") - verifyQueueHasNoExcessiveElements(clearPackageResultsQueue, "clearPackage") + verifyQueueHasNoExcessiveElements(installApplicationResultsQueue, FUNCTION_INSTALL_APPLICATION) + verifyQueueHasNoExcessiveElements(gettingDeviceStatusResultsQueue, FUNCTION_DEVICE_STATUS) + verifyQueueHasNoExcessiveElements(runTestsResultsQueue, FUNCTION_RUN_ISOLATED_TEST) + verifyQueueHasNoExcessiveElements(clearPackageResultsQueue, FUNCTION_CLEAR_PACKAGE) } private fun resultQueuePrecondition(queue: Queue<*>, functionName: String, values: String) { @@ -197,6 +202,10 @@ public open class StubDevice( } public companion object { + private const val FUNCTION_INSTALL_APPLICATION = "installApplication" + private const val FUNCTION_DEVICE_STATUS = "deviceStatus" + private const val FUNCTION_RUN_ISOLATED_TEST = "runIsolatedTest" + private const val FUNCTION_CLEAR_PACKAGE = "clearPackage" public fun installApplicationSuccess(applicationPackage: String = "doesntmatter"): Result { return Result.Success(