io.kotest.extensions.allure.AllureWriter.kt Maven / Gradle / Ivy
The newest version!
package io.kotest.extensions.allure
import io.kotest.core.config.ProjectConfiguration
import io.kotest.core.descriptors.Descriptor
import io.kotest.core.descriptors.TestPath
import io.kotest.core.test.TestCase
import io.kotest.core.test.TestResult
import io.kotest.engine.test.names.DefaultDisplayNameFormatter
import io.kotest.engine.test.names.FallbackDisplayNameFormatter
import io.kotest.engine.test.names.formatTestPath
import io.qameta.allure.Allure
import io.qameta.allure.AllureLifecycle
import io.qameta.allure.model.Status
import io.qameta.allure.model.StatusDetails
import io.qameta.allure.model.StepResult
import io.qameta.allure.util.ResultsUtils
import java.lang.reflect.InvocationTargetException
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
class AllureWriter {
companion object {
const val LanguageLabel = "kotlin"
const val FrameworkLabel = "kotest"
}
private val formatter = FallbackDisplayNameFormatter(
DefaultDisplayNameFormatter(ProjectConfiguration().apply {
includeTestScopeAffixes = true
})
)
/**
* Loads the [AllureLifecycle] object which is used to report test lifecycle events.
*/
val allure = try {
Allure.getLifecycle() ?: throw IllegalStateException("Allure lifecycle was null")
} catch (t: Throwable) {
t.printStackTrace()
throw t
}
private val uuids = ConcurrentHashMap()
fun id(testCase: TestCase) = uuids[testCase.descriptor.path()]
fun startTestCase(testCase: TestCase) {
val labels = listOfNotNull(
testCase.epic(),
testCase.feature(),
ResultsUtils.createFrameworkLabel(FrameworkLabel),
ResultsUtils.createHostLabel(),
ResultsUtils.createLanguageLabel(LanguageLabel),
ResultsUtils.createTestClassLabel(testCase.spec::class.java.simpleName),
testCase.owner(),
ResultsUtils.createPackageLabel(testCase.spec::class.java.`package`.name),
ResultsUtils.createSuiteLabel(testCase.descriptor.spec().id.value),
testCase.maxSeverity()?.let { ResultsUtils.createSeverityLabel(it) },
testCase.story(),
ResultsUtils.createThreadLabel(),
)
val links = links(testCase)
val uuid = UUID.randomUUID().toString()
uuids[testCase.descriptor.path()] = uuid
val result = io.qameta.allure.model.TestResult()
.setFullName(formatter.formatTestPath(testCase, " / "))
.setName(formatter.formatTestPath(testCase, " "))
.setUuid(uuid)
.setTestCaseId(safeId(testCase.descriptor))
.setHistoryId(safeId(testCase.descriptor))
.setLabels(labels)
.setLinks(links)
.setDescription(testCase.description())
allure.scheduleTestCase(result)
allure.startTestCase(uuid)
}
fun finishTestCase(testCase: TestCase, result: TestResult) {
val status = when (result) {
// what we call an error, allure calls broken
is TestResult.Error -> Status.BROKEN
is TestResult.Failure -> Status.FAILED
is TestResult.Ignored -> Status.SKIPPED
is TestResult.Success -> Status.PASSED
}
val uuid = uuids[testCase.descriptor.path()] ?: "Unknown test ${testCase.descriptor}"
val details = ResultsUtils.getStatusDetails(result.errorOrNull)
allure.stopTestCase(uuid)
allure.updateTestCase(uuid) {
it.status = status
it.statusDetails = details.orElseGet { null }
testCase.descriptor.parents().forEach { d ->
it.steps.add(
StepResult()
.setName(d.id.value)
.setStatus(Status.PASSED)
.setStart(0L)
.setStop(0L)
)
}
}
allure.writeTestCase(uuid)
}
private fun links(kclass: KClass<*>): List {
return listOfNotNull(
kclass.issue(),
kclass.link(),
) + kclass.links()
}
private fun links(testCase: TestCase): List {
return listOfNotNull(
testCase.issue(),
testCase.link(),
) + testCase.links()
}
fun allureResultSpecInitFailure(kclass: KClass<*>, t: Throwable) {
val uuid = UUID.randomUUID()
val labels = listOfNotNull(
ResultsUtils.createSuiteLabel(kclass.qualifiedName),
ResultsUtils.createThreadLabel(),
ResultsUtils.createHostLabel(),
ResultsUtils.createLanguageLabel(LanguageLabel),
ResultsUtils.createFrameworkLabel(FrameworkLabel),
ResultsUtils.createPackageLabel(kclass.java.`package`.name),
kclass.severity(),
kclass.owner(),
kclass.epic(),
kclass.feature(),
kclass.story()
)
val links = links(kclass)
val result = io.qameta.allure.model.TestResult()
.setFullName(kclass.qualifiedName)
.setName(kclass.simpleName)
.setUuid(uuid.toString())
.setLabels(labels)
.setLinks(links)
allure.scheduleTestCase(result)
allure.startTestCase(uuid.toString())
val instanceError = (t.cause as InvocationTargetException).targetException
val details = StatusDetails()
details.message = instanceError?.message ?: "Unknown error"
var trace = ""
instanceError.stackTrace?.forEach {
trace += "$it\n"
}
details.trace = trace
allure.updateTestCase(uuid.toString()) {
it.status = Status.FAILED
it.statusDetails = details
}
allure.stopTestCase(uuid.toString())
allure.writeTestCase(uuid.toString())
}
// returns an id that's acceptable in format for allure
private fun safeId(descriptor: Descriptor.TestDescriptor): String = descriptor.path(true).value
}