aboutsummaryrefslogtreecommitdiffstats
path: root/dashboard/app/coverage.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2025-01-23 21:54:41 +0100
committerTaras Madan <tarasmadan@google.com>2025-01-27 10:05:21 +0000
commit2bf68614de1620ef12f086d9e86d5c8b334bf32d (patch)
tree24b69669a1ee44c1a34988d917cb248faed40cd3 /dashboard/app/coverage.go
parent0868754a9d325ba9011e1cb74510f68d4b627c79 (diff)
dashboard/app: test coverage /file link
1. Init coveragedb client once and propagate it through context to enable mocking. 2. Always init coverage handlers. It simplifies testing. 3. Read webGit and coveragedb client from ctx to make it mockable. 4. Use int for file line number and int64 for merged coverage. 5. Add tests.
Diffstat (limited to 'dashboard/app/coverage.go')
-rw-r--r--dashboard/app/coverage.go77
1 files changed, 66 insertions, 11 deletions
diff --git a/dashboard/app/coverage.go b/dashboard/app/coverage.go
index 7bc0d2a06..c91c7d665 100644
--- a/dashboard/app/coverage.go
+++ b/dashboard/app/coverage.go
@@ -8,6 +8,7 @@ import (
"fmt"
"html/template"
"net/http"
+ "os"
"slices"
"strconv"
@@ -17,8 +18,36 @@ import (
"github.com/google/syzkaller/pkg/coveragedb/spannerclient"
"github.com/google/syzkaller/pkg/covermerger"
"github.com/google/syzkaller/pkg/validator"
+ "google.golang.org/appengine/v2"
)
+var coverageDBClient spannerclient.SpannerClient
+
+func initCoverageDB() {
+ if !appengine.IsAppEngine() {
+ // It is a test environment.
+ // Use SetCoverageDBClient to specify the coveragedb mock or emulator in every test.
+ return
+ }
+ projectID := os.Getenv("GOOGLE_CLOUD_PROJECT")
+ var err error
+ coverageDBClient, err = spannerclient.NewClient(context.Background(), projectID)
+ if err != nil {
+ panic("spanner.NewClient: " + err.Error())
+ }
+}
+
+var keyCoverageDBClient = "coveragedb client key"
+
+func SetCoverageDBClient(ctx context.Context, client spannerclient.SpannerClient) context.Context {
+ return context.WithValue(ctx, &keyCoverageDBClient, client)
+}
+
+func GetCoverageDBClient(ctx context.Context) spannerclient.SpannerClient {
+ client, _ := ctx.Value(&keyCoverageDBClient).(spannerclient.SpannerClient)
+ return client
+}
+
type funcStyleBodyJS func(
ctx context.Context, client spannerclient.SpannerClient,
scope *cover.SelectScope, onlyUnique bool, sss, managers []string,
@@ -37,6 +66,10 @@ func handleHeatmap(c context.Context, w http.ResponseWriter, r *http.Request, f
if err != nil {
return err
}
+ nsConfig := getNsConfig(c, hdr.Namespace)
+ if nsConfig.Coverage == nil {
+ return ErrClientNotFound
+ }
ss := r.FormValue("subsystem")
manager := r.FormValue("manager")
@@ -76,15 +109,9 @@ func handleHeatmap(c context.Context, w http.ResponseWriter, r *http.Request, f
onlyUnique := r.FormValue("unique-only") == "1"
- spannerClient, err := spannerclient.NewClient(c, "syzkaller")
- if err != nil {
- return fmt.Errorf("spanner.NewClient: %s", err.Error())
- }
- defer spannerClient.Close()
-
var style template.CSS
var body, js template.HTML
- if style, body, js, err = f(c, spannerClient,
+ if style, body, js, err = f(c, GetCoverageDBClient(c),
&cover.SelectScope{
Ns: hdr.Namespace,
Subsystem: ss,
@@ -147,24 +174,37 @@ func handleFileCoverage(c context.Context, w http.ResponseWriter, r *http.Reques
}
onlyUnique := r.FormValue("unique-only") == "1"
mainNsRepo, _ := nsConfig.mainRepoBranch()
- hitLines, hitCounts, err := coveragedb.ReadLinesHitCount(c, hdr.Namespace, targetCommit, manager, kernelFilePath, tp)
+ client := GetCoverageDBClient(c)
+ if client == nil {
+ return fmt.Errorf("spannerdb client is nil")
+ }
+ hitLines, hitCounts, err := coveragedb.ReadLinesHitCount(
+ c, client, hdr.Namespace, targetCommit, kernelFilePath, manager, tp)
covMap := cover.MakeCovMap(hitLines, hitCounts)
if err != nil {
return fmt.Errorf("coveragedb.ReadLinesHitCount(%s): %w", manager, err)
}
if onlyUnique {
- allHitLines, allHitCounts, err := coveragedb.ReadLinesHitCount(c, hdr.Namespace, targetCommit, manager, kernelFilePath, tp)
+ // This request is expected to be made second by tests.
+ // Moving it to goroutine don't forget to change multiManagerCovDBFixture.
+ allHitLines, allHitCounts, err := coveragedb.ReadLinesHitCount(
+ c, client, hdr.Namespace, targetCommit, kernelFilePath, "*", tp)
if err != nil {
return fmt.Errorf("coveragedb.ReadLinesHitCount(*): %w", err)
}
covMap = cover.UniqCoverage(cover.MakeCovMap(allHitLines, allHitCounts), covMap)
}
+ webGit := getWebGit(c) // Get mock if available.
+ if webGit == nil {
+ webGit = covermerger.MakeWebGit(makeProxyURIProvider(nsConfig.Coverage.WebGitURI))
+ }
+
content, err := cover.RendFileCoverage(
mainNsRepo,
targetCommit,
kernelFilePath,
- makeProxyURIProvider(nsConfig.Coverage.WebGitURI),
+ webGit,
&covermerger.MergeResult{HitCounts: covMap},
cover.DefaultHTMLRenderConfig())
if err != nil {
@@ -175,11 +215,26 @@ func handleFileCoverage(c context.Context, w http.ResponseWriter, r *http.Reques
return nil
}
+var keyWebGit = "file content provider"
+
+func setWebGit(ctx context.Context, provider covermerger.FileVersProvider) context.Context {
+ return context.WithValue(ctx, &keyWebGit, provider)
+}
+
+func getWebGit(ctx context.Context) covermerger.FileVersProvider {
+ res, _ := ctx.Value(&keyWebGit).(covermerger.FileVersProvider)
+ return res
+}
+
func handleCoverageGraph(c context.Context, w http.ResponseWriter, r *http.Request) error {
hdr, err := commonHeader(c, r, w, "")
if err != nil {
return err
}
+ nsConfig := getNsConfig(c, hdr.Namespace)
+ if nsConfig.Coverage == nil {
+ return ErrClientNotFound
+ }
periodType := r.FormValue("period")
if periodType == "" {
periodType = coveragedb.QuarterPeriod
@@ -187,7 +242,7 @@ func handleCoverageGraph(c context.Context, w http.ResponseWriter, r *http.Reque
if periodType != coveragedb.QuarterPeriod && periodType != coveragedb.MonthPeriod {
return fmt.Errorf("only quarter and month are allowed, but received %s instead", periodType)
}
- hist, err := MergedCoverage(c, hdr.Namespace, periodType)
+ hist, err := MergedCoverage(c, GetCoverageDBClient(c), hdr.Namespace, periodType)
if err != nil {
return err
}