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 @@ -15,32 +15,52 @@
*/
package com.google.firebase.gradle.plugins.report

import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive

/**
* Represents a single run of a test in CI. One unit/instrumentation test workflow run creates many
* `TestReport`s, one for each tested SDK.
*
* @param name SDK name of the associated test run.
* @param type What type of test result this is, either unit or instrumentation test.
* @param type What type of test result this is, either unit, instrumentation or daily test.
* @param status Conclusion status of the test run, `SUCCESS`/`FAILURE` for typical results, `OTHER`
* for ongoing runs and unexpected data.
* @param commit Commit SHA this test was run on.
* @param url Link to the GHA test run info, including logs.
* @param date The ISO date of the run, only if the test is a daily test
*/
data class TestReport(
val name: String,
val type: Type,
val status: Status,
val commit: String,
val url: String,
val date: String? = null,
) {
enum class Type {
UNIT_TEST,
INSTRUMENTATION_TEST,
DAILY_TEST,
}

enum class Status {
SUCCESS,
FAILURE,
OTHER,
OTHER;

companion object {
fun of(json: JsonObject): Status {
if (json["status"]?.jsonPrimitive?.content == "completed") {
if (json["conclusion"]?.jsonPrimitive?.content == "success") {
return SUCCESS
} else {
return FAILURE
}
} else {
return OTHER
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,33 @@ class TestReportGenerator(private val apiToken: String) {
?.int ?: throw RuntimeException("Couldn't find PR number for commit $obj"),
)
}
outputReport(outputFile, commits)
val dailyTests = arrayListOf<TestReport>()
// GraphQL does not expose actions in an easy to index way, and the REST API does
val dailyTestResponse = request("actions/workflows/ai-daily-tests.yml/runs?per_page=7")
val arr =
dailyTestResponse["workflow_runs"]?.jsonArray
?: throw RuntimeException("Workflow request failed")
for (test in arr) {
val obj = test.jsonObject
dailyTests.add(
TestReport(
"ai-daily-test",
TestReport.Type.DAILY_TEST,
TestReport.Status.of(obj),
obj["head_commit"]?.jsonObject?.get("id")?.jsonPrimitive?.content
?: throw RuntimeException("Missing head_commit"),
obj["html_url"]?.jsonPrimitive?.content ?: throw RuntimeException("Missing url"),
)
)
}
outputReport(outputFile, commits, dailyTests)
}

private fun outputReport(outputFile: File, commits: List<ReportCommit>) {
private fun outputReport(
outputFile: File,
commits: List<ReportCommit>,
dailyTests: List<TestReport>,
) {
val reports = commits.flatMap { commit -> parseTestReports(commit.sha) }
val output = StringBuilder()
output.append("### Unit Tests\n\n")
Expand All @@ -97,6 +120,9 @@ class TestReportGenerator(private val apiToken: String) {
)
)
output.append("\n")
output.append("### Daily Tests\n\n")
output.append(generateDailyTable(dailyTests))
output.append("\n")

try {
outputFile.writeText(output.toString())
Expand Down Expand Up @@ -192,6 +218,42 @@ class TestReportGenerator(private val apiToken: String) {
return output.toString()
}

private fun generateDailyTable(reports: List<TestReport>): String {
val output = StringBuilder("| |")
var offset = 0
for (report in reports) {
output.append(" -${offset++}")
output.append(" |")
}
output.append(" Success Rate |\n|")
output.append(" :--- |")
output.append(" :---: |".repeat(reports.size))
output.append(" :--- |")
output.append("\n| AI Daily Tests |")
for (report in reports) {
val icon =
when (report.status) {
TestReport.Status.SUCCESS -> "✅"
TestReport.Status.FAILURE -> "⛔"
TestReport.Status.OTHER -> "➖"
}
val link: String = " [%s](%s)".format(icon, report.url)
output.append(link)
output.append(" |")
}
output.append(" ")
val successChance: Int =
reports.filter { r -> r.status == TestReport.Status.SUCCESS }.size * 100 / reports.size
if (successChance == 100) {
output.append("✅ 100%")
} else {
output.append("⛔ $successChance%")
}
output.append(" |")
output.append("\n")
return output.toString()
}

private fun parseTestReports(commit: String): List<TestReport> {
val runs = request("actions/runs?head_sha=$commit")
for (el in runs["workflow_runs"] as JsonArray) {
Expand Down Expand Up @@ -234,16 +296,7 @@ class TestReportGenerator(private val apiToken: String) {
.dropLastWhile { it.isEmpty() }
.toTypedArray()[1]
name = name.substring(0, name.length - 1) // Remove trailing ")"
val status =
if (job["status"]?.jsonPrimitive?.content == "completed") {
if (job["conclusion"]?.jsonPrimitive?.content == "success") {
TestReport.Status.SUCCESS
} else {
TestReport.Status.FAILURE
}
} else {
TestReport.Status.OTHER
}
val status = TestReport.Status.of(job)
val url = job["html_url"]?.jsonPrimitive?.content ?: throw RuntimeException("PR missing URL")
return TestReport(name, type, status, commit, url)
}
Expand Down
Loading