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
Binary file added reports/marathon/marathon.tar.gz
Binary file not shown.
33 changes: 13 additions & 20 deletions src/main/kotlin/fm/unit/UnitApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import fm.unit.dao.Organizations
import fm.unit.dao.PayloadArgumentFactory
import fm.unit.dao.Reports
import fm.unit.dao.Repositories
import fm.unit.model.ProjectSummary
import fm.unit.dao.Testsuites
import fm.unit.model.Project
import fm.unit.model.Report
import fm.unit.model.Testsuite
import io.ktor.application.Application
Expand Down Expand Up @@ -35,28 +36,17 @@ import io.ktor.server.netty.Netty
import io.ktor.velocity.Velocity
import io.ktor.velocity.VelocityContent
import kotlinx.coroutines.runBlocking
import mu.KotlinLogging
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
import org.jdbi.v3.core.Jdbi
import org.jdbi.v3.sqlobject.kotlin.onDemand
import kotlin.random.Random


fun Application.module() {
val logger = KotlinLogging.logger {}

/**
* For DEBUGging purposes only. Method generates a list of fake reports to be able to test template generation locally
*/
fun testReports(random: Random = Random(1)): List<Report> {
fun summaries(random: Random): List<Testsuite.Summary> {
return (0..random.nextInt(10)).map {
Testsuite.Summary(tests = random.nextInt(1, 10), errors = random.nextInt(2))
}
}

return listOf("Fake News!", "Cake Is a Lie", "Magic Unicorn").map { Report(it, summaries(random)) }
}

fun template(summary: ProjectSummary): VelocityContent {
fun template(summary: Project.Summary): VelocityContent {
val template = "templates/reports/summary.vm"
val model = mutableMapOf<String, Any>("summary" to summary)
return VelocityContent(template, model)
Expand Down Expand Up @@ -136,14 +126,17 @@ fun Application.module() {
val repository = call.parameters["repository"]!!
val prefix = call.parameters["prefix"]!!

val orgId = jdbi.onDemand<Organizations>().read(organization)
val repoId = jdbi.onDemand<Repositories>().read(repository)
val orgId = runBlocking { jdbi.onDemand<Organizations>().read(organization) }
val repoId = runBlocking { jdbi.onDemand<Repositories>().read(repository) }

if (orgId == null || repoId == null) {
call.respond(HttpStatusCode.NotFound)
} else {
val reports = testReports()// TODO(karsten): fetch from database
val summary = ProjectSummary(reports)
val reportSummaries = runBlocking {
jdbi.onDemand<Reports>().readReportSummaries(orgId, repoId, prefix)
}
logger.debug { "Visualizing summaries: $reportSummaries" }
val summary = Project.Summary(reportSummaries)
call.respond(template(summary))
}
}
Expand All @@ -163,7 +156,7 @@ fun Application.module() {
val multipart = call.receiveMultipart()
val (commit_hash, suites) = readPostedReport(multipart)
runBlocking {
jdbi.onDemand<Reports>().create(orgId, repoId, prefix, commit_hash, suites)
jdbi.onDemand<Reports>().create(orgId, repoId, commit_hash, prefix, suites)
}

call.respond(HttpStatusCode.Created)
Expand Down
18 changes: 14 additions & 4 deletions src/main/kotlin/fm/unit/dao/Reports.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fm.unit.dao

import fm.unit.model.Report
import fm.unit.model.Testsuite
import org.jdbi.v3.sqlobject.CreateSqlObject
import org.jdbi.v3.sqlobject.statement.GetGeneratedKeys
Expand Down Expand Up @@ -48,8 +49,17 @@ interface Reports {
return report_id
}


// TODO(karsten): join with testsuites.
@SqlQuery("SELECT (commit_hash) FROM reports ORDER BY report_id")
fun reports(): List<String>
@SqlQuery("""
SELECT reports.report_id,
SUM((xpath('count(//testcase)', payload))[1]::text::integer) AS ts_tests,
SUM((xpath('count(//failure)', payload))[1]::text::integer) AS ts_errors
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should use COUNT(xpath('//testcase')) instead. At some point we probably want to fetch all errors anyways so this will change.

FROM reports
LEFT JOIN testsuites ON reports.report_id = testsuites.report_id
WHERE reports.prefix = :prefix
AND reports.organization_id = :organization_id
AND reports.repository_id = :repository_id
GROUP BY reports.report_id
ORDER BY reports.report_id
""")
fun readReportSummaries(organization_id: Int, repository_id: Int, prefix: String): List<Report.Summary>
}
11 changes: 11 additions & 0 deletions src/main/kotlin/fm/unit/model/Project.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fm.unit.model

import fm.unit.model.Report

object Project {

/**
* A summary of the last n reports.
*/
data class Summary(val reports: List<Report.Summary>)
}
17 changes: 15 additions & 2 deletions src/main/kotlin/fm/unit/model/Report.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package fm.unit.model

import com.sun.org.apache.xpath.internal.operations.Bool
import org.jdbi.v3.core.mapper.Nested

/**
* A report belongs to a build of a repo in an organization.
*/
data class Report(val prefix: String, val testsuites: List<Testsuite.Summary>) {
data class Report(val prefix: String) {


/**
* A summary of the last n reports.
*/
data class Summary(val report_id: Int, @Nested("ts") val testsuiteSummary: Testsuite.Summary) {

val errors: Int = testsuites.sumBy { it.errors }
/**
* Indicates whether all testsuites succeeded.
*/
val successful: Boolean = testsuiteSummary.errors == 0
}
}
11 changes: 0 additions & 11 deletions src/main/kotlin/fm/unit/model/Summary.kt

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/resources/templates/reports/summary.vm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<svg width="100%" height="100%">
#foreach($report in $summary.reports)
#set($outcome = "#if($report.errors == 0)success#{else}failure#end")
#set($outcome = "#if($report.successful)success#{else}failure#end")
#set($x = $foreach.count * 10)

<g class="build" x="10">
Expand Down
40 changes: 36 additions & 4 deletions src/test/kotlin/fm/unit/dao/ReportsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ import io.kotlintest.matchers.collections.shouldHaveSize
import io.kotlintest.specs.StringSpec
import org.jdbi.v3.sqlobject.kotlin.onDemand
import fm.unit.kotlintest.listeners.JdbiFixture
import fm.unit.model.Report
import fm.unit.model.Testsuite
import io.kotlintest.matchers.collections.shouldContain
import io.kotlintest.shouldBe
import java.io.File

class ReportsTest: StringSpec() {
// Setup database
val db = JdbiFixture()
override fun listeners(): List<TestListener> = listOf(db)

val exceptionXml = File("fixtures/exception.xml").bufferedReader().use { it.readText() }
val passXml = File("fixtures/pass.xml").bufferedReader().use { it.readText() }

init {
"Reports DAO roundtrip" {

// Given
val org_dao = db.jdbi.onDemand<Organizations>()
val org_id = org_dao.create("jeschkies")

Expand All @@ -27,12 +34,37 @@ class ReportsTest: StringSpec() {
repo_dao.read("unknown") shouldBe (null)
repo_dao.read("unit") shouldBe (repo_id)

// When
val dao = db.jdbi.onDemand<Reports>()
dao.create(org_id, repo_id, "deadbeaf", "system-test")
dao.create(org_id, repo_id,"12345678", "system-test")
dao.create(org_id, repo_id,"12345678", "integration-test")

// Then
dao.readReportSummaries(org_id, repo_id, "system-test") shouldHaveSize (2)
dao.readReportSummaries(org_id, repo_id, "integration-test") shouldHaveSize (1)
}

"Summarize last n reports" {
// Given
val org_id = db.jdbi.onDemand<Organizations>().create("jeschkies")
val repo_id = db.jdbi.onDemand<Repositories>().create("unit")

val reportsDao = db.jdbi.onDemand<Reports>()
val suites = listOf(
Testsuite("exception.xml", Testsuite.Payload(exceptionXml)),
Testsuite("pass.xml", Testsuite.Payload(passXml))
)
val report_id1 = reportsDao.create(org_id, repo_id, "deadbeaf", "system-test", suites)
val report_id2 = reportsDao.create(org_id, repo_id, "12345678", "system-test", suites.takeLast(1))

dao.create(org_id, repo_id, "deadbeaf", "/jeschkies/unit")
dao.create(org_id, repo_id,"12345678", "/jeschkies/unit")
// When
val summaries = reportsDao.readReportSummaries(org_id, repo_id, "system-test")

dao.reports() shouldHaveSize (2)
// Then
summaries shouldHaveSize (2)
summaries shouldContain (Report.Summary(report_id1, Testsuite.Summary(6, 1)))
summaries shouldContain (Report.Summary(report_id2, Testsuite.Summary(3, 0)))
}
}
}
28 changes: 16 additions & 12 deletions src/test/kotlin/fm/unit/dao/TestsuitesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fm.unit.kotlintest.listeners.JdbiFixture
import fm.unit.model.Testsuite
import io.kotlintest.extensions.TestListener
import io.kotlintest.matchers.collections.shouldContain
import io.kotlintest.shouldBe
import io.kotlintest.specs.StringSpec
import java.io.File
import org.jdbi.v3.sqlobject.kotlin.onDemand
Expand All @@ -15,24 +16,27 @@ class TestsuitesTest: StringSpec() {

override fun listeners(): List<TestListener> = listOf(db)

val exceptionXml = File("fixtures/exception.xml").bufferedReader().use { it.readText() }
val passXml = File("fixtures/pass.xml").bufferedReader().use { it.readText() }

init {
"Testsuites DAO roundtrip" {
db.jdbi.registerArgument(PayloadArgumentFactory)

val org_dao = db.jdbi.onDemand<Organizations>()
val org_id = org_dao.create("jeschkies")
// Given
val orgDao = db.jdbi.onDemand<Organizations>()
val org_id = orgDao.create("jeschkies")

val repo_dao = db.jdbi.onDemand<Repositories>()
val repo_id = repo_dao.create("unit")
val repoDao = db.jdbi.onDemand<Repositories>()
val repo_id = repoDao.create("unit")

val reports_dao = db.jdbi.onDemand<Reports>()
val report_id = reports_dao.create(org_id, repo_id,"deadbeaf", "/jeschkies/unit")
val reportsDao = db.jdbi.onDemand<Reports>()
val report_id = reportsDao.create(org_id, repo_id,"deadbeaf", "system-test")

val suite_dao = db.jdbi.onDemand<Testsuites>()
val xmlFile = File("fixtures/exception.xml").bufferedReader().use { it.readText() }
suite_dao.create(report_id, Testsuite("exception.xml", Testsuite.Payload(xmlFile)))
// When
val suiteDao = db.jdbi.onDemand<Testsuites>()
suiteDao.create(report_id, Testsuite("exception.xml", Testsuite.Payload(exceptionXml)))

val summaries = suite_dao.summaries()
// Then
val summaries = suiteDao.summaries()
summaries shouldContain(Testsuite.Summary(3, 1))
}
}
Expand Down
12 changes: 8 additions & 4 deletions src/test/kotlin/fm/unit/kotlintest/listeners/JdbiFixture.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package fm.unit.kotlintest.listeners

import com.opentable.db.postgres.embedded.EmbeddedPostgres
import fm.unit.dao.PayloadArgumentFactory
import io.kotlintest.Description
import io.kotlintest.Spec
import io.kotlintest.TestResult
import io.kotlintest.extensions.TestListener
import org.flywaydb.core.Flyway
Expand All @@ -20,19 +22,21 @@ class JdbiFixture : TestListener {
val flyway = Flyway.configure().dataSource(dataSource).load()
val jdbi: Jdbi = Jdbi.create(dataSource)

override fun beforeTest(description: Description): Unit {
override fun beforeSpec(description: Description, spec: Spec) {
flyway.migrate()
jdbi.installPlugins()
jdbi.registerArgument(PayloadArgumentFactory)

super.beforeSpec(description, spec)
}

override fun afterTest(description: Description, result: TestResult) {
override fun afterSpec(description: Description, spec: Spec) {
try {
epg.close()
} catch (e: IOException) {
throw AssertionError(e)
}

super.afterTest(description, result)
super.afterSpec(description, spec)
}

}
Loading