Skip to content
Draft
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
@@ -1,10 +1,12 @@
{{define "printTable"}}
{{ $jobRunIDToNumber := .JobRunIDToNumber }}
{{ $data := .Data }}
<table>
<tr>
<th>Name</th>
<th>Job Runs</th>
</tr>
{{ range . }}
{{ range $data }}
{{ $jobRuns := .JobRuns }}
{{ $formatedSummary := (.Summary | formatSummary)}}
{{ $failedQuantileData := ( "" | parseQuantileValues) }}
Expand Down Expand Up @@ -49,12 +51,15 @@
{{ range $jobRuns }}
{{ $jobName := .JobName}}
{{ $jobID := .JobRunID}}
{{ $jobNumber := getJobRunNumber $jobRunIDToNumber $jobID }}
<div class="job-item">
<a href="{{ .HumanURL }}" target="_blank" style="text-decoration:none;">
<div class="tooltip {{ toLower .Status }}">
<span class="job-number">{{ $jobNumber }}</span>
<div class="tooltiptext">
<p>{{ $jobName }}</p>
<p>ID: {{ $jobID }}</p>
<p>Number: {{ $jobNumber }}</p>
{{ with $failedQuantileData }}
{{ if mapHasKey . $jobID }}
<div>Quantile Value: {{ (index . $jobID) }}</div>
Expand Down Expand Up @@ -112,8 +117,8 @@
position: relative;
display: inline-block;
background-color: #0c3;
min-width: 20px;
min-height: 20px;
min-width: 24px;
min-height: 24px;
max-width: 500px;
color: white;
border-radius: 5px;
Expand Down Expand Up @@ -189,9 +194,22 @@
display: inline-block;
margin: 5px 0px;
}
.job-number {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-weight: bold;
font-size: 11px;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
pointer-events: none;
user-select: none;
}
</style>
</head>
<body>
{{ $jobRunIDToNumber := .JobRunIDToNumber }}
{{ $failedData := infoForTestSuite .JobName .InitialParents .Suite isFailed }}
{{ $skippedData := infoForTestSuite .JobName .InitialParents .Suite isSkipped }}
{{ $successData := infoForTestSuite .JobName .InitialParents .Suite isSuccess }}
Expand All @@ -208,7 +226,7 @@
<div>
<h2 id="failed-tests">Failed Tests</h2>
</div>
{{ template "printTable" .}}
{{ template "printTable" (dict "Data" . "JobRunIDToNumber" $jobRunIDToNumber)}}
</div>
{{end}}

Expand All @@ -217,7 +235,7 @@
<div>
<h2 id="skipped-tests">Skipped Tests</h2>
</div>
{{ template "printTable" .}}
{{ template "printTable" (dict "Data" . "JobRunIDToNumber" $jobRunIDToNumber)}}
</div>
{{end}}

Expand All @@ -227,7 +245,7 @@
<h2 id="passed-tests">Passed Tests</h2>
</div>
<div style="padding: 10px;">
{{ template "printTable" .}}
{{ template "printTable" (dict "Data" . "JobRunIDToNumber" $jobRunIDToNumber)}}
</div>
{{end}}
</body>
Expand Down
77 changes: 71 additions & 6 deletions pkg/jobrunaggregator/jobrunaggregatoranalyzer/spyglass_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"html/template"
"regexp"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -35,6 +36,23 @@ var (
"isSuccess": func() testCaseFilterFunc { return isSuccess },
"infoForTestSuite": infoForTestSuite,
"toLower": strings.ToLower,
"getJobRunNumber": func(jobRunIDToNumber map[string]int, jobRunID string) int {
return jobRunIDToNumber[jobRunID]
},
"dict": func(values ...interface{}) (map[string]interface{}, error) {
if len(values)%2 != 0 {
return nil, fmt.Errorf("dict requires an even number of arguments")
}
dict := make(map[string]interface{}, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].(string)
if !ok {
return nil, fmt.Errorf("dict keys must be strings")
}
dict[key] = values[i+1]
}
return dict, nil
},
"mapHasKey": func(value map[string]string, key string) bool {
_, found := value[key]
return found
Expand Down Expand Up @@ -145,14 +163,23 @@ var (

// if someone has the HTML skills, making this a mini-test grid would be awesome.
func htmlForTestRuns(jobName string, suite *junit.TestSuite) (string, error) {
// Collect and number all job runs
allJobRunIDs := collectAllJobRunIDs(suite)
jobRunIDToNumber := make(map[string]int)
for i, jobRunID := range allJobRunIDs {
jobRunIDToNumber[jobRunID] = i + 1
}

data := struct {
JobName string
Suite *junit.TestSuite
InitialParents []string
JobName string
Suite *junit.TestSuite
InitialParents []string
JobRunIDToNumber map[string]int
}{
JobName: jobName,
Suite: suite,
InitialParents: []string{},
JobName: jobName,
Suite: suite,
InitialParents: []string{},
JobRunIDToNumber: jobRunIDToNumber,
}
buff := bytes.Buffer{}
err := htmlTemplate.Execute(&buff, data)
Expand Down Expand Up @@ -265,3 +292,41 @@ func infoForTestCase(jobName string, parents []string, testCase *junit.TestCase,

return &testInfo
}

// collectAllJobRunIDs traverses the test suite tree and collects all unique JobRunIDs
func collectAllJobRunIDs(suite *junit.TestSuite) []string {
jobRunIDSet := sets.Set[string]{}
collectJobRunIDsFromSuite(suite, jobRunIDSet)

// Sort alphabetically for stable ordering
jobRunIDs := sets.List(jobRunIDSet)
sort.Strings(jobRunIDs)

return jobRunIDs
}

// collectJobRunIDsFromSuite recursively extracts JobRunIDs from test cases
func collectJobRunIDsFromSuite(suite *junit.TestSuite, jobRunIDs sets.Set[string]) {
// Extract JobRunIDs from test case details (passes, failures, skips)
for _, testCase := range suite.TestCases {
currDetails := &jobrunaggregatorlib.TestCaseDetails{}
if len(testCase.SystemOut) > 0 {
_ = yaml.Unmarshal([]byte(testCase.SystemOut), currDetails)
}

for _, failure := range currDetails.Failures {
jobRunIDs.Insert(failure.JobRunID)
}
for _, pass := range currDetails.Passes {
jobRunIDs.Insert(pass.JobRunID)
}
for _, skip := range currDetails.Skips {
jobRunIDs.Insert(skip.JobRunID)
}
}

// Recursively process child suites
for _, child := range suite.Children {
collectJobRunIDsFromSuite(child, jobRunIDs)
}
}