diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt index 2e7bdce77..0e35df843 100644 --- a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt @@ -18,6 +18,7 @@ package com.epam.drill.agent.configuration import com.epam.drill.agent.common.configuration.AgentParameterDefinition import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition +import com.epam.drill.agent.konform.validation.jsonschema.minLength object ParameterDefinitions: AgentParameterDefinitionCollection() { @@ -64,6 +65,13 @@ object ParameterDefinitions: AgentParameterDefinitionCollection() { val JS_AGENT_BUILD_VERSION = NullableAgentParameterDefinition.forString(name = "jsAgentBuildVersion").register() val JS_AGENT_ID = NullableAgentParameterDefinition.forString(name = "jsAgentId").register() + val TEST_PROJECT_ID = AgentParameterDefinition.forString( + name = "testProjectId", + description = "Unique arbitrary string identifying your test project. Example: my-test-project", + validator = { + identifier() + minLength(3) + }).register() val TEST_TASK_ID = AgentParameterDefinition.forString(name = "testTaskId", defaultValue = "").register() val RECOMMENDED_TESTS_ENABLED = AgentParameterDefinition.forBoolean(name = "recommendedTestsEnabled", defaultValue = false).register() val RECOMMENDED_TESTS_TARGET_APP_ID = AgentParameterDefinition.forString(name = "recommendedTestsTargetAppId", defaultValue = "").register() diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt index d6fc9f689..cf76ca417 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/TestExecutionRecorder.kt @@ -32,5 +32,7 @@ interface TestExecutionRecorder { fun getFinishedTests(): List + fun getStartedTests(): List + fun reset() } \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt index d09d59e5d..f013247fd 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/execution/ThreadTestExecutionRecorder.kt @@ -30,6 +30,7 @@ class ThreadTestExecutionRecorder( private val listeners: List = emptyList() ) : TestExecutionRecorder { private val logger = KotlinLogging.logger {} + private val testDefinitionData: ConcurrentHashMap = ConcurrentHashMap() private val testExecutionData: ConcurrentHashMap = ConcurrentHashMap() private val testLaunchHolder: ThreadLocal = ThreadLocal.withInitial { null } @@ -38,6 +39,7 @@ class ThreadTestExecutionRecorder( ) { val testLaunchId = generateTestLaunchId() testLaunchHolder.set(testLaunchId) + testDefinitionData.computeIfAbsent(testMethod.signature) { testMethod } updateTestInfo(testLaunchId, testMethod) { it.startedAt = System.currentTimeMillis() } @@ -87,6 +89,12 @@ class ThreadTestExecutionRecorder( testExecutionData.clear() } + override fun getStartedTests(): List { + return testDefinitionData.onEach { + testExecutionData.remove(it.key) + }.values.toList() + } + override fun getFinishedTests(): List = testExecutionData .filterValues { test -> test.result != TestResult.UNKNOWN } .onEach { diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestsPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestDefinitionsPayload.kt similarity index 86% rename from java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestsPayload.kt rename to java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestDefinitionsPayload.kt index a67a6a89b..d0c0e7495 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestsPayload.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestDefinitionsPayload.kt @@ -19,8 +19,8 @@ import com.epam.drill.agent.common.transport.AgentMessage import kotlinx.serialization.Serializable @Serializable -data class AddTestsPayload( +class AddTestDefinitionsPayload( val groupId: String, - val sessionId: String, - val tests: List = emptyList(), + val testProjectId: String? = null, + val definitions: List ): AgentMessage() \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestLaunchesPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestLaunchesPayload.kt new file mode 100644 index 000000000..2e3afd1b5 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/AddTestLaunchesPayload.kt @@ -0,0 +1,27 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.test.sending + +import com.epam.drill.agent.common.transport.AgentMessage +import kotlinx.serialization.Serializable + +@Serializable +class AddTestLaunchesPayload( + val groupId: String, + val testProjectId: String? = null, + val testSessionId: String, + val launches: List, +): AgentMessage() \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt index 00e68fa46..dfa7f297b 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestDefinitionPayload.kt @@ -19,10 +19,11 @@ import kotlinx.serialization.Serializable @Serializable class TestDefinitionPayload( - val runner: String = "", - val path: String = "", - val testName: String = "", - val testParams: List = emptyList(), - val metadata: Map = emptyMap(), + val id: String, + val runner: String, + val name: String, + val type: String? = null, + val path: String?, val tags: List = emptyList(), + val metadata: Map = emptyMap(), ) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt index 9914153d0..b0a408dea 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestInfoSender.kt @@ -20,7 +20,7 @@ import com.epam.drill.agent.common.transport.AgentMessageSender import com.epam.drill.agent.configuration.Configuration import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.ParameterDefinitions -import com.epam.drill.agent.test.session.SessionController +import com.epam.drill.agent.test.session.SessionController.getSessionId import mu.KotlinLogging import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -33,7 +33,8 @@ interface TestInfoSender { class IntervalTestInfoSender( private val messageSender: AgentMessageSender, private val intervalMs: Long = 1000, - private val collectTests: () -> List = { emptyList() } + private val collectTestDefinitions: () -> List, + private val collectTestLaunches: () -> List, ) : TestInfoSender { private val logger = KotlinLogging.logger {} private val scheduledThreadPool = Executors.newSingleThreadScheduledExecutor { r -> @@ -46,9 +47,14 @@ class IntervalTestInfoSender( scheduledThreadPool.scheduleAtFixedRate( { try { - sendTests(collectTests()) + sendTestDefinitions(collectTestDefinitions()) } catch (t: Throwable) { - logger.error(t) { "Test sending job failed" } + logger.error(t) { "Test definition sending job failed" } + } + try { + sendTestLaunches(collectTestLaunches()) + } catch (t: Throwable) { + logger.error(t) { "Test launch sending job failed" } } }, 0, @@ -59,7 +65,8 @@ class IntervalTestInfoSender( } override fun stopSendingTests(remainingMs: Long) { - sendTests(collectTests()) + sendTestDefinitions(collectTestDefinitions()) + sendTestLaunches(collectTestLaunches()) scheduledThreadPool.shutdown() if (remainingMs > 0 && !scheduledThreadPool.awaitTermination(remainingMs, TimeUnit.MILLISECONDS)) { logger.warn { "Test sending scheduler did not stop within ${remainingMs}ms; leaving it for JVM exit." } @@ -67,17 +74,32 @@ class IntervalTestInfoSender( logger.info { "Test sending job is stopped." } } - private fun sendTests(tests: List) { - if (tests.isEmpty()) return - logger.debug { "Sending ${tests.size} tests..." } + private fun sendTestLaunches(launches: List) { + if (launches.isEmpty()) return + logger.debug { "Sending ${launches.size} test launches..." } + messageSender.send( + destination = AgentMessageDestination("POST", "test-launches"), + message = AddTestLaunchesPayload( + groupId = Configuration.parameters[DefaultParameterDefinitions.GROUP_ID], + testProjectId = Configuration.parameters[ParameterDefinitions.TEST_PROJECT_ID], + testSessionId = getSessionId(), + launches = launches + ), + serializer = AddTestLaunchesPayload.serializer() + ) + } + + private fun sendTestDefinitions(definitions: List) { + if (definitions.isEmpty()) return + logger.debug { "Sending ${definitions.size} test definitions..." } messageSender.send( - destination = AgentMessageDestination("POST", "tests-metadata"), - message = AddTestsPayload( + destination = AgentMessageDestination("POST", "test-definitions"), + message = AddTestDefinitionsPayload( groupId = Configuration.parameters[DefaultParameterDefinitions.GROUP_ID], - sessionId = SessionController.getSessionId(), - tests = tests + testProjectId = Configuration.parameters[ParameterDefinitions.TEST_PROJECT_ID], + definitions = definitions ), - serializer = AddTestsPayload.serializer() + serializer = AddTestDefinitionsPayload.serializer() ) } } \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt index ee2196eea..0acfcc116 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/sending/TestLaunchPayload.kt @@ -15,14 +15,12 @@ */ package com.epam.drill.agent.test.sending -import com.epam.drill.agent.test.execution.TestResult import kotlinx.serialization.Serializable @Serializable data class TestLaunchPayload( - val testLaunchId: String, + val id: String, val testDefinitionId: String, - val result: TestResult, - val duration: Int?, - val details: TestDefinitionPayload, + val result: String?, + val duration: Int? = null, ) \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt index b34e44aaa..73021d817 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionController.kt @@ -31,6 +31,10 @@ import com.epam.drill.agent.test.execution.TestExecutionInfo import com.epam.drill.agent.test.sending.TestDefinitionPayload import com.epam.drill.agent.test.sending.TestLaunchPayload import com.epam.drill.agent.common.lifecycle.AgentShutdownRegistry +import com.epam.drill.agent.test.execution.TestMethodInfo +import com.epam.drill.agent.test.sending.AddTestDefinitionsPayload +import com.epam.drill.agent.test.sending.AddTestLaunchesPayload +import com.epam.drill.agent.test.session.SessionController.getSessionId import com.epam.drill.agent.transport.DataIngestMessageSender import mu.KotlinLogging import java.time.Instant @@ -46,7 +50,8 @@ actual object SessionController { ) private val testInfoSender: TestInfoSender = IntervalTestInfoSender( messageSender = DataIngestMessageSender, - collectTests = { TestController.getFinishedTests().toTestLaunchPayloads() } + collectTestDefinitions = { TestController.getStartedTests().toTestDefinitionPayloads() }, + collectTestLaunches = { TestController.getFinishedTests().toTestLaunchPayloads() } ) private lateinit var sessionId: String @@ -80,6 +85,7 @@ actual object SessionController { SessionPayload( id = sessionId, groupId = Configuration.parameters[DefaultParameterDefinitions.GROUP_ID], + testProjectId = Configuration.parameters[ParameterDefinitions.TEST_PROJECT_ID], testTaskId = Configuration.parameters[ParameterDefinitions.TEST_TASK_ID], startedAt = System.currentTimeMillis().toIsoTimeFormat(), builds = builds @@ -90,24 +96,27 @@ actual object SessionController { fun getSessionId(): String = sessionId private fun isTestTracingEnabled(): Boolean = Configuration.parameters[TEST_TRACING_ENABLED] - private fun isTestLaunchMetadataSendingEnabled(): Boolean = isTestTracingEnabled() && Configuration.parameters[ParameterDefinitions.TEST_TRACING_PER_TEST_LAUNCH_ENABLED] + private fun isTestLaunchMetadataSendingEnabled(): Boolean = + isTestTracingEnabled() && Configuration.parameters[ParameterDefinitions.TEST_TRACING_PER_TEST_LAUNCH_ENABLED] } private fun List.toTestLaunchPayloads(): List = map { info -> - val testDefinitionPayload = TestDefinitionPayload( - runner = info.testMethod.engine, - path = info.testMethod.className, - testName = info.testMethod.method, - testParams = info.testMethod.methodParams.removeSurrounding("(", ")").split(",").filter { it.isNotEmpty() }, - metadata = info.testMethod.metadata, - tags = info.testMethod.tags - ) TestLaunchPayload( - testLaunchId = info.testLaunchId, + id = info.testLaunchId, testDefinitionId = hash(info.testMethod.signature), - result = info.result, + result = info.result.name, duration = info.finishedAt?.minus(info.startedAt ?: 0)?.toInt(), - details = testDefinitionPayload + ) +} + +private fun List.toTestDefinitionPayloads(): List = map { info -> + TestDefinitionPayload( + id = hash(info.signature), + runner = info.engine, + name = info.method, + path = info.className, + tags = info.tags, + metadata = info.metadata ) } diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt index 8f142f84f..032036c85 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/test/session/SessionPayload.kt @@ -30,6 +30,7 @@ class SingleSessionBuildPayload( class SessionPayload( val id: String, val groupId: String, + val testProjectId: String, val testTaskId: String, val startedAt: String, val builds: List = emptyList()