]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/tls: add flag to render HTML BoGo report
authorDaniel McCarney <daniel@binaryparadox.net>
Mon, 30 Jun 2025 16:45:36 +0000 (12:45 -0400)
committerGopher Robot <gobot@golang.org>
Fri, 3 Oct 2025 15:29:57 +0000 (08:29 -0700)
Updates the BoGo test runner to add a `-bogo-html-report` flag. When
provided, an HTML report is written to the flag argument path. The
report shows the fail/pass/skip status of run tests and allows
sorting/searching the output.

Change-Id: I8c704a51fbb03500f4134ebfaba06248baa3ca2f
Reviewed-on: https://go-review.googlesource.com/c/go/+/684955
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
TryBot-Bypass: Daniel McCarney <daniel@binaryparadox.net>
Commit-Queue: Carlos Amedee <carlos@golang.org>

src/crypto/tls/bogo_shim_test.go
src/crypto/tls/handshake_test.go

index 7cab568db80953b03e524376ad2f9c5c9f0a2844..f3f19b34e9e4ccbc58160a0a8ec0bc9290c5cbc4 100644 (file)
@@ -13,6 +13,7 @@ import (
        "encoding/pem"
        "flag"
        "fmt"
+       "html/template"
        "internal/byteorder"
        "internal/testenv"
        "io"
@@ -25,10 +26,13 @@ import (
        "strconv"
        "strings"
        "testing"
+       "time"
 
        "golang.org/x/crypto/cryptobyte"
 )
 
+const boringsslModVer = "v0.0.0-20250620172916-f51d8b099832"
+
 var (
        port   = flag.String("port", "", "")
        server = flag.Bool("server", false, "")
@@ -557,7 +561,6 @@ func TestBogoSuite(t *testing.T) {
        if *bogoLocalDir != "" {
                bogoDir = *bogoLocalDir
        } else {
-               const boringsslModVer = "v0.0.0-20250620172916-f51d8b099832"
                bogoDir = cryptotest.FetchModule(t, "boringssl.googlesource.com/boringssl.git", boringsslModVer)
        }
 
@@ -606,6 +609,12 @@ func TestBogoSuite(t *testing.T) {
                t.Fatalf("failed to parse results JSON: %s", err)
        }
 
+       if *bogoReport != "" {
+               if err := generateReport(results, *bogoReport); err != nil {
+                       t.Fatalf("failed to generate report: %v", err)
+               }
+       }
+
        // assertResults contains test results we want to make sure
        // are present in the output. They are only checked if -bogo-filter
        // was not passed.
@@ -655,6 +664,23 @@ func TestBogoSuite(t *testing.T) {
        }
 }
 
+func generateReport(results bogoResults, outPath string) error {
+       data := reportData{
+               Results:   results,
+               Timestamp: time.Unix(int64(results.SecondsSinceEpoch), 0).Format("2006-01-02 15:04:05"),
+               Revision:  boringsslModVer,
+       }
+
+       tmpl := template.Must(template.New("report").Parse(reportTemplate))
+       file, err := os.Create(outPath)
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       return tmpl.Execute(file, data)
+}
+
 // bogoResults is a copy of boringssl.googlesource.com/boringssl/testresults.Results
 type bogoResults struct {
        Version           int            `json:"version"`
@@ -669,3 +695,127 @@ type bogoResults struct {
                Error        string `json:"error,omitempty"`
        } `json:"tests"`
 }
+
+type reportData struct {
+       Results     bogoResults
+       SkipReasons map[string]string
+       Timestamp   string
+       Revision    string
+}
+
+const reportTemplate = `
+<!DOCTYPE html>
+<html>
+<head>
+    <title>BoGo Results Report</title>
+    <style>
+        body { font-family: monospace; margin: 20px; }
+        .summary { background: #f5f5f5; padding: 10px; margin-bottom: 20px; }
+        .controls { margin-bottom: 10px; }
+        .controls input, select { margin-right: 10px; }
+        table { width: 100%; border-collapse: collapse; table-layout: fixed; }
+        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; vertical-align: top; }
+        th { background-color: #f2f2f2; cursor: pointer; }
+        .name-col { width: 30%; }
+        .status-col { width: 8%; }
+        .actual-col { width: 8%; }
+        .expected-col { width: 8%; }
+        .error-col { width: 26%; }
+        .PASS { background-color: #d4edda; }
+        .FAIL { background-color: #f8d7da; }
+        .SKIP { background-color: #fff3cd; }
+        .error {
+            font-family: monospace;
+            font-size: 0.9em;
+            color: #721c24;
+            white-space: pre-wrap;
+            word-break: break-word;
+        }
+    </style>
+</head>
+<body>
+<h1>BoGo Results Report</h1>
+
+<div class="summary">
+    <strong>Generated:</strong> {{.Timestamp}} | <strong>BoGo Revision:</strong> {{.Revision}}<br>
+    {{range $status, $count := .Results.NumFailuresByType}}
+    <strong>{{$status}}:</strong> {{$count}} |
+    {{end}}
+</div>
+
+<div class="controls">
+    <input type="text" id="search" placeholder="Search tests..." onkeyup="filterTests()">
+    <select id="statusFilter" onchange="filterTests()">
+        <option value="">All</option>
+        <option value="FAIL">Failed</option>
+        <option value="PASS">Passed</option>
+        <option value="SKIP">Skipped</option>
+    </select>
+</div>
+
+<table id="resultsTable">
+    <thead>
+    <tr>
+        <th class="name-col" onclick="sortBy('name')">Test Name</th>
+        <th class="status-col" onclick="sortBy('status')">Status</th>
+        <th class="actual-col" onclick="sortBy('actual')">Actual</th>
+        <th class="expected-col" onclick="sortBy('expected')">Expected</th>
+        <th class="error-col">Error</th>
+    </tr>
+    </thead>
+    <tbody>
+    {{range $name, $test := .Results.Tests}}
+    <tr class="{{$test.Actual}}" data-name="{{$name}}" data-status="{{$test.Actual}}">
+        <td>{{$name}}</td>
+        <td>{{$test.Actual}}</td>
+        <td>{{$test.Actual}}</td>
+        <td>{{$test.Expected}}</td>
+        <td class="error">{{$test.Error}}</td>
+    </tr>
+    {{end}}
+    </tbody>
+</table>
+
+<script>
+    function filterTests() {
+        const search = document.getElementById('search').value.toLowerCase();
+        const status = document.getElementById('statusFilter').value;
+        const rows = document.querySelectorAll('#resultsTable tbody tr');
+
+        rows.forEach(row => {
+            const name = row.dataset.name.toLowerCase();
+            const rowStatus = row.dataset.status;
+            const matchesSearch = name.includes(search);
+            const matchesStatus = !status || rowStatus === status;
+
+            row.style.display = matchesSearch && matchesStatus ? '' : 'none';
+        });
+    }
+
+    function sortBy(column) {
+        const tbody = document.querySelector('#resultsTable tbody');
+        const rows = Array.from(tbody.querySelectorAll('tr'));
+
+        rows.sort((a, b) => {
+            if (column === 'status') {
+                const statusOrder = {'FAIL': 0, 'PASS': 1, 'SKIP': 2};
+                const aStatus = a.dataset.status;
+                const bStatus = b.dataset.status;
+                if (aStatus !== bStatus) {
+                    return statusOrder[aStatus] - statusOrder[bStatus];
+                }
+                return a.dataset.name.localeCompare(b.dataset.name);
+            } else {
+                return a.dataset.name.localeCompare(b.dataset.name);
+            }
+        });
+
+        rows.forEach(row => tbody.appendChild(row));
+        filterTests();
+    }
+
+    sortBy("status");
+</script>
+</body>
+</html>
+`
index ea8ac6fc837ae780329472c0412d15e6f0dfbaec..13f1bb1dd50a19d0311fa7b9f256d045a8407265 100644 (file)
@@ -47,6 +47,7 @@ var (
        bogoMode     = flag.Bool("bogo-mode", false, "Enabled bogo shim mode, ignore everything else")
        bogoFilter   = flag.String("bogo-filter", "", "BoGo test filter")
        bogoLocalDir = flag.String("bogo-local-dir", "", "Local BoGo to use, instead of fetching from source")
+       bogoReport   = flag.String("bogo-html-report", "", "File path to render an HTML report with BoGo results")
 )
 
 func runTestAndUpdateIfNeeded(t *testing.T, name string, run func(t *testing.T, update bool), wait bool) {