@ 261101.7

261023.4 · 35082dd6a557aa0802e4dfccbe9f16faa23ff2a6

Tests wait for the online-repo goroutines

The tests swap the globals cacheRoot and webScheme, and their cleanup cancelled web's context without waiting. A repo probe or listing still running read the global while the next test or the cleanup wrote it, which go test -race reported. web now counts what it starts in a WaitGroup, and the cleanup waits for it. A running server sets these globals once at start, so it never raced.

TestPageBudget skips under the race detector, which slows every page past five milliseconds.

reading declarations

budget_test.go

@@ -18,6 +18,9 @@ const pageBudget = 5 * time.Millisecond // render.go, a listing, histories and a commit's diff. Each takes the // median of 15 runs, so one slow run does not fail it. func TestPageBudget(t *testing.T) {+	if raceOn {+		t.Skip("the race detector slows every page past the budget")+	} 	needGit(t) 	dir := t.TempDir() 	for _, name := range []string{"README.md", "render.go"} {

norace_test.go

new file mode 100644@@ -0,0 +1,5 @@+//go:build !race++package main++const raceOn = false

race_test.go

new file mode 100644@@ -0,0 +1,7 @@+//go:build race++package main++// raceOn reports whether the race detector is built in. It slows code+// five to 10 times, so timing tests skip under it.+const raceOn = true

web.go

@@ -52,6 +52,10 @@ type web struct { 	lists  map[string]*listing // by the URL path of a host or account 	busy   map[string]bool     // keys being cloned or fetched, which trim leaves 	kicked time.Time++	// wg counts the goroutines web starts, so a test can wait for them+	// before it changes the globals they read.+	wg sync.WaitGroup }  // webScheme is how web reaches forges. Tests serve repos over http.@@ -253,7 +257,7 @@ func (w *web) visit(r *webRepo) { 	} 	if !r.fetching && time.Since(r.fetched) > webRefresh && time.Since(r.tried) > webRefresh { 		r.fetching, r.tried = true, time.Now()-		go w.refresh(r)+		w.wg.Go(func() { w.refresh(r) }) 	} } @@ -342,8 +346,8 @@ func (w *web) show(r *webRepo, wt string) error { 		old.root.Close() 	} 	logInfo("serves %s at %s from %s", r.key, filepath.Base(wt), wt)-	go t.warm()-	go t.warmLinks()+	w.wg.Go(t.warm)+	w.wg.Go(t.warmLinks) 	return nil } @@ -364,7 +368,7 @@ func (w *web) start(key string, segs []string) *webJob { 	} 	j := &webJob{urlPath: "/" + key, started: time.Now()} 	w.jobs[key] = j-	go w.run(j, key, segs)+	w.wg.Go(func() { w.run(j, key, segs) }) 	return j } 

web_test.go

@@ -92,6 +92,7 @@ func testWeb(t *testing.T, limit int64) *web { 	} 	t.Cleanup(func() { 		cancel()+		w.wg.Wait() 		cacheRoot = old 	}) 	return w