Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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() {

Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ interface TestExecutionRecorder {

fun getFinishedTests(): List<TestExecutionInfo>

fun getStartedTests(): List<TestMethodInfo>

fun reset()
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ThreadTestExecutionRecorder(
private val listeners: List<TestExecutionListener> = emptyList()
) : TestExecutionRecorder {
private val logger = KotlinLogging.logger {}
private val testDefinitionData: ConcurrentHashMap<String, TestMethodInfo> = ConcurrentHashMap()
private val testExecutionData: ConcurrentHashMap<String, TestExecutionInfo> = ConcurrentHashMap()
private val testLaunchHolder: ThreadLocal<String> = ThreadLocal.withInitial { null }

Expand All @@ -38,6 +39,7 @@ class ThreadTestExecutionRecorder(
) {
val testLaunchId = generateTestLaunchId()
testLaunchHolder.set(testLaunchId)
testDefinitionData.computeIfAbsent(testMethod.signature) { testMethod }
updateTestInfo(testLaunchId, testMethod) {
it.startedAt = System.currentTimeMillis()
}
Expand Down Expand Up @@ -87,6 +89,12 @@ class ThreadTestExecutionRecorder(
testExecutionData.clear()
}

override fun getStartedTests(): List<TestMethodInfo> {
return testDefinitionData.onEach {
testExecutionData.remove(it.key)
}.values.toList()
}

override fun getFinishedTests(): List<TestExecutionInfo> = testExecutionData
.filterValues { test -> test.result != TestResult.UNKNOWN }
.onEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestLaunchPayload> = emptyList(),
val testProjectId: String? = null,
val definitions: List<TestDefinitionPayload>
): AgentMessage()
Original file line number Diff line number Diff line change
@@ -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<TestLaunchPayload>,
): AgentMessage()
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import kotlinx.serialization.Serializable

@Serializable
class TestDefinitionPayload(
val runner: String = "",
val path: String = "",
val testName: String = "",
val testParams: List<String> = emptyList(),
val metadata: Map<String, String> = emptyMap(),
val id: String,
val runner: String,
val name: String,
val type: String? = null,
val path: String?,
val tags: List<String> = emptyList(),
val metadata: Map<String, String> = emptyMap(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,7 +33,8 @@ interface TestInfoSender {
class IntervalTestInfoSender(
private val messageSender: AgentMessageSender,
private val intervalMs: Long = 1000,
private val collectTests: () -> List<TestLaunchPayload> = { emptyList() }
private val collectTestDefinitions: () -> List<TestDefinitionPayload>,
private val collectTestLaunches: () -> List<TestLaunchPayload>,
) : TestInfoSender {
private val logger = KotlinLogging.logger {}
private val scheduledThreadPool = Executors.newSingleThreadScheduledExecutor { r ->
Expand All @@ -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,
Expand All @@ -59,25 +65,41 @@ 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." }
}
logger.info { "Test sending job is stopped." }
}

private fun sendTests(tests: List<TestLaunchPayload>) {
if (tests.isEmpty()) return
logger.debug { "Sending ${tests.size} tests..." }
private fun sendTestLaunches(launches: List<TestLaunchPayload>) {
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<TestDefinitionPayload>) {
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()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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<TestExecutionInfo>.toTestLaunchPayloads(): List<TestLaunchPayload> = 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<TestMethodInfo>.toTestDefinitionPayloads(): List<TestDefinitionPayload> = map { info ->
TestDefinitionPayload(
id = hash(info.signature),
runner = info.engine,
name = info.method,
path = info.className,
tags = info.tags,
metadata = info.metadata
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SingleSessionBuildPayload> = emptyList()
Expand Down
Loading