Skip to content

Test report reworked (#522) #523

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 18, 2022
Merged
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 @@ -145,7 +145,7 @@ class GenerateTestsCommand :
SourceFindingStrategyDefault(classFqn, sourceCodeFile, testsFilePath, projectRootPath)
val report = SarifReport(testCases, testClassBody, sourceFinding).createReport()
saveToFile(report, sarifReport)
println("The report was saved to \"$sarifReport\". You can open it using the VS Code extension \"Sarif Viewer\".")
println("The report was saved to \"$sarifReport\".")
}
}
} catch (t: Throwable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,29 @@ data class TestsGenerationReport(
get() = executables.firstOrNull()?.clazz
?: error("No executables found in test report")

// Summary message is generated lazily to avoid evaluation of classUnderTest
var summaryMessage: () -> String = { "Unit tests for $classUnderTest were generated successfully." }
val initialWarnings: MutableList<() -> String> = mutableListOf()
val hasWarnings: Boolean
get() = initialWarnings.isNotEmpty()

val detailedStatistics: String
get() = buildString {
appendHtmlLine("Class: ${classUnderTest.qualifiedName}")
val testMethodsStatistic = executables.map { it.countTestMethods() }
val errors = executables.map { it.countErrors() }
val overallErrors = errors.sum()

appendHtmlLine("Successful test methods: ${testMethodsStatistic.sumBy { it.successful }}")
appendHtmlLine(
"Failing because of unexpected exception test methods: ${testMethodsStatistic.sumBy { it.failing }}"
)
appendHtmlLine(
"Failing because of exceeding timeout test methods: ${testMethodsStatistic.sumBy { it.timeout }}"
)
appendHtmlLine(
"Failing because of possible JVM crash test methods: ${testMethodsStatistic.sumBy { it.crashes }}"
)
appendHtmlLine("Not generated because of internal errors test methods: $overallErrors")
}

fun addMethodErrors(testCase: UtTestCase, errors: Map<String, Int>) {
this.errors[testCase.method] = errors
Expand All @@ -216,61 +236,24 @@ data class TestsGenerationReport(
}
}

override fun toString(): String = buildString {
appendHtmlLine(summaryMessage())
appendHtmlLine()
initialWarnings.forEach { appendHtmlLine(it()) }
appendHtmlLine()
fun toString(isShort: Boolean): String = buildString {
appendHtmlLine("Target: ${classUnderTest.qualifiedName}")
if (initialWarnings.isNotEmpty()) {
initialWarnings.forEach { appendHtmlLine(it()) }
appendHtmlLine()
}

val testMethodsStatistic = executables.map { it.countTestMethods() }
val errors = executables.map { it.countErrors() }
val overallTestMethods = testMethodsStatistic.sumBy { it.count }
val overallErrors = errors.sum()

appendHtmlLine("Overall test methods: $overallTestMethods")
appendHtmlLine("Successful test methods: ${testMethodsStatistic.sumBy { it.successful }}")
appendHtmlLine(
"Failing because of unexpected exception test methods: ${testMethodsStatistic.sumBy { it.failing }}"
)
appendHtmlLine(
"Failing because of exceeding timeout test methods: ${testMethodsStatistic.sumBy { it.timeout }}"
)
appendHtmlLine(
"Failing because of possible JVM crash test methods: ${testMethodsStatistic.sumBy { it.crashes }}"
)
appendHtmlLine("Not generated because of internal errors test methods: $overallErrors")
}

// TODO: should we use TsvWriter from univocity instead of this manual implementation?
fun getFileContent(): String =
(listOf(getHeader()) + getLines()).joinToString(System.lineSeparator())

private fun getHeader(): String {
val columnNames = listOf(
"Executable/Number of test methods",
SUCCESSFUL,
FAILING,
TIMEOUT,
CRASH,
"Errors tests"
)

return columnNames.joinToString(TAB_SEPARATOR)
if (!isShort) {
appendHtmlLine(detailedStatistics)
}
}

private fun getLines(): List<String> =
executables.map { executable ->
val testMethodStatistic = executable.countTestMethods()
with(testMethodStatistic) {
listOf(
executable,
successful,
failing,
timeout,
crashes,
executable.countErrors()
).joinToString(TAB_SEPARATOR)
}
}
override fun toString(): String = toString(false)

private fun UtMethod<*>.countTestMethods(): TestMethodStatistic = TestMethodStatistic(
testMethodsNumber(successfulExecutions),
Expand All @@ -291,9 +274,4 @@ data class TestsGenerationReport(
private data class TestMethodStatistic(val successful: Int, val failing: Int, val timeout: Int, val crashes: Int) {
val count: Int = successful + failing + timeout + crashes
}

companion object {
private const val TAB_SEPARATOR: String = "\t"
const val EXTENSION: String = ".tsv"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class GenerateTestsAndSarifReportFacade(
mergedSarifReportFile.writeText(mergedReport)
if (verbose) {
println("SARIF report was saved to \"${mergedSarifReportFile.path}\"")
println("You can open it using the VS Code extension \"Sarif Viewer\"")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Computable
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.JavaDirectoryService
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassOwner
Expand Down Expand Up @@ -47,7 +46,6 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.scripting.resolve.classId
import org.utbot.common.HTML_LINE_SEPARATOR
import org.utbot.common.PathUtil.classFqnToPath
import org.utbot.common.PathUtil.toHtmlLinkTag
import org.utbot.common.appendHtmlLine
import org.utbot.framework.codegen.Import
Expand All @@ -56,7 +54,6 @@ import org.utbot.framework.codegen.RegularImport
import org.utbot.framework.codegen.StaticImport
import org.utbot.framework.codegen.model.CodeGenerator
import org.utbot.framework.codegen.model.TestsCodeWithTestReport
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.UtTestCase
Expand All @@ -70,16 +67,17 @@ import org.utbot.intellij.plugin.models.GenerateTestsModel
import org.utbot.intellij.plugin.models.packageName
import org.utbot.intellij.plugin.sarif.SarifReportIdea
import org.utbot.intellij.plugin.sarif.SourceFindingStrategyIdea
import org.utbot.intellij.plugin.ui.DetailsTestsReportNotifier
import org.utbot.intellij.plugin.ui.SarifReportNotifier
import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener
import org.utbot.intellij.plugin.ui.TestsReportNotifier
import org.utbot.intellij.plugin.ui.WarningTestsReportNotifier
import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
import org.utbot.intellij.plugin.ui.utils.getOrCreateTestResourcesPath
import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater
import org.utbot.intellij.plugin.util.signature
import org.utbot.sarif.SarifReport
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.reflect.KClass
Expand Down Expand Up @@ -167,7 +165,6 @@ object CodeGenerationController {
SarifReportNotifier.notify(
info = """
SARIF report was saved to ${toHtmlLinkTag(mergedReportFile.path)}$HTML_LINE_SEPARATOR
You can open it using the VS Code extension "Sarif Viewer"
""".trimIndent()
)
}
Expand Down Expand Up @@ -394,32 +391,10 @@ object CodeGenerationController {
"Test resources directory $testResourcesDirPath does not exist"
}

val testReportSubDir = "utbot-tests-report"
val classFqn = with(testsCodeWithTestReport.testsGenerationReport.classUnderTest) {
qualifiedName ?: error("Could not save tests report for anonymous or local class $this")
}
val fileReportPath = classFqnToPath(classFqn)

val resultedReportedPath =
Paths.get(
testResourcesDirPath.toString(),
testReportSubDir,
fileReportPath + "TestReport" + TestsGenerationReport.EXTENSION
)

val parent = resultedReportedPath.parent
requireNotNull(parent) {
"Expected from parent of $resultedReportedPath to be not null but it is null"
}

VfsUtil.createDirectories(parent.toString())
resultedReportedPath.toFile().writeText(testsCodeWithTestReport.testsGenerationReport.getFileContent())

processInitialWarnings(testsCodeWithTestReport, model)

val notifyMessage = buildString {
appendHtmlLine(testsCodeWithTestReport.testsGenerationReport.toString())
appendHtmlLine()
appendHtmlLine(testsCodeWithTestReport.testsGenerationReport.toString(isShort = true))
val classUnderTestPackageName =
testsCodeWithTestReport.testsGenerationReport.classUnderTest.classId.packageFqName.toString()
if (classUnderTestPackageName != model.testPackageName) {
Expand All @@ -430,12 +405,19 @@ object CodeGenerationController {
appendHtmlLine(warningMessage)
appendHtmlLine()
}
val savedFileMessage = """
Tests report was saved to ${toHtmlLinkTag(resultedReportedPath.toString())} in TSV format
val eventLogMessage = """
<a href="${TestReportUrlOpeningListener.prefix}${TestReportUrlOpeningListener.eventLogSuffix}">See details in Event Log</a>.
""".trimIndent()
appendHtmlLine(savedFileMessage)
appendHtmlLine(eventLogMessage)
}

if (testsCodeWithTestReport.testsGenerationReport.hasWarnings) {
WarningTestsReportNotifier.notify(notifyMessage)
} else {
TestsReportNotifier.notify(notifyMessage)
}
TestsReportNotifier.notify(notifyMessage)

DetailsTestsReportNotifier.notify(testsCodeWithTestReport.testsGenerationReport.detailedStatistics)
}

private fun processInitialWarnings(testsCodeWithTestReport: TestsCodeWithTestReport, model: GenerateTestsModel) {
Expand All @@ -445,8 +427,6 @@ object CodeGenerationController {
}

testsCodeWithTestReport.testsGenerationReport.apply {
summaryMessage = { "Unit tests for $classUnderTest were generated with warnings.<br>" }

if (model.forceMockHappened) {
initialWarnings.add {
"""
Expand Down
Loading