mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-17 12:19:07 +02:00
Management / Unit (amd64, mysql) hit the 20 minute go test budget on #7516. The package was not hung: each of the 133 test store creations in management/server paid about 1.6s on MySQL for CREATE DATABASE, the pre-migrations, a 40-table AutoMigrate and the post-migrations, which puts the package at 10 minutes on a healthy runner and over the budget on a slow one. The migration now runs once per test binary into a template database and each test database is cloned from it, with CREATE DATABASE ... TEMPLATE on Postgres and a replay of SHOW CREATE TABLE on MySQL. The MySQL test container also drops the binary log, doublewrite buffer and per-commit redo fsync. Two goroutine leaks in the test helpers are fixed. tools/gotestsummary turns the go test -json stream into a readable log, and the Management unit and integration jobs now pipe through it, so a timeout names the tests still running. On MySQL, management/server went from 10m16s to 6m36s.
181 lines
6.8 KiB
Go
181 lines
6.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func feed(t *testing.T, events string) string {
|
|
t.Helper()
|
|
var out bytes.Buffer
|
|
s := newSummarizer(&out)
|
|
if err := s.consume(strings.NewReader(events)); err != nil {
|
|
t.Fatalf("consume: %v", err)
|
|
}
|
|
s.printSummary(10)
|
|
return out.String()
|
|
}
|
|
|
|
func TestTimeoutReportsUnfinishedTestsAndPanicHead(t *testing.T) {
|
|
events := `
|
|
{"Action":"run","Package":"a","Test":"TestHang"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"=== RUN TestHang\n"}
|
|
{"Action":"run","Package":"a","Test":"TestSilent"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"panic: test timed out after 1s\n"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"\trunning tests:\n"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"\t\tTestHang (1s)\n"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"goroutine 7 [running]:\n"}
|
|
{"Action":"fail","Package":"a","Elapsed":1.0}
|
|
`
|
|
got := feed(t, events)
|
|
for _, want := range []string{
|
|
"--- UNFINISHED: a.TestHang",
|
|
"--- UNFINISHED: a.TestSilent",
|
|
"==== panic in a (first 4 lines) ====",
|
|
"\t\tTestHang (1s)",
|
|
" === RUN TestHang",
|
|
} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("output lacks %q:\n%s", want, got)
|
|
}
|
|
}
|
|
if strings.Contains(got, " goroutine 7 [running]:") {
|
|
t.Errorf("goroutine dump leaked into the test's own output:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestPanicInOnePackageKeepsOtherPackageOutput(t *testing.T) {
|
|
events := `
|
|
{"Action":"run","Package":"a","Test":"TestHang"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"panic: test timed out after 1s\n"}
|
|
{"Action":"run","Package":"b","Test":"TestOther"}
|
|
{"Action":"output","Package":"b","Test":"TestOther","Output":" other_test.go:9: expected 1, got 2\n"}
|
|
{"Action":"output","Package":"a","Test":"TestHang","Output":"goroutine 7 [running]:\n"}
|
|
{"Action":"fail","Package":"b","Test":"TestOther","Elapsed":0.01}
|
|
{"Action":"fail","Package":"b","Elapsed":0.02}
|
|
{"Action":"fail","Package":"a","Elapsed":1.0}
|
|
`
|
|
got := feed(t, events)
|
|
if !strings.Contains(got, " other_test.go:9: expected 1, got 2") {
|
|
t.Errorf("other package's output was swallowed by the panic head:\n%s", got)
|
|
}
|
|
if strings.Contains(got, "panic in b") {
|
|
t.Errorf("panic head attributed to the wrong package:\n%s", got)
|
|
}
|
|
if !strings.Contains(got, "==== panic in a (first 2 lines) ====") {
|
|
t.Errorf("panic head missing for package a:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestBuildFailureShowsCompilerOutput(t *testing.T) {
|
|
// The event sequence go test emits for a build failure: the build events
|
|
// name the test binary, then the package itself fails with FailedBuild.
|
|
events := `
|
|
{"Action":"build-output","ImportPath":"a [a.test]","Output":"# a [a.test]\na_test.go:7:2: undefined: nope\na_test.go:9:2: undefined: nope2\n"}
|
|
{"Action":"build-fail","ImportPath":"a [a.test]"}
|
|
{"Action":"start","Package":"a"}
|
|
{"Action":"output","Package":"a","Output":"FAIL\ta [build failed]\n"}
|
|
{"Action":"fail","Package":"a","Elapsed":0,"FailedBuild":"a [a.test]"}
|
|
`
|
|
got := feed(t, events)
|
|
for _, want := range []string{
|
|
"==== build output of a ====",
|
|
" a_test.go:7:2: undefined: nope\n a_test.go:9:2: undefined: nope2",
|
|
"FAIL\ta [build failed]",
|
|
} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("output lacks %q:\n%s", want, got)
|
|
}
|
|
}
|
|
if n := strings.Count(got, "FAIL a 0s"); n != 1 {
|
|
t.Errorf("expected one FAIL line for the package, got %d:\n%s", n, got)
|
|
}
|
|
if n := strings.Count(got, "undefined: nope2"); n != 1 {
|
|
t.Errorf("expected the compiler output once, got %d:\n%s", n, got)
|
|
}
|
|
}
|
|
|
|
func TestFailedDependencyOutputIsShownForEveryImporter(t *testing.T) {
|
|
events := `
|
|
{"Action":"build-output","ImportPath":"m/x","Output":"# m/x\nx.go:3:11: undefined: y\n"}
|
|
{"Action":"build-fail","ImportPath":"m/x"}
|
|
{"Action":"start","Package":"m/a"}
|
|
{"Action":"output","Package":"m/a","Output":"FAIL\tm/a [build failed]\n"}
|
|
{"Action":"fail","Package":"m/a","Elapsed":0,"FailedBuild":"m/x"}
|
|
{"Action":"start","Package":"m/b"}
|
|
{"Action":"output","Package":"m/b","Output":"FAIL\tm/b [build failed]\n"}
|
|
{"Action":"fail","Package":"m/b","Elapsed":0,"FailedBuild":"m/x"}
|
|
`
|
|
got := feed(t, events)
|
|
if n := strings.Count(got, "x.go:3:11: undefined: y"); n != 2 {
|
|
t.Errorf("expected the dependency's compiler output under both packages, got %d:\n%s", n, got)
|
|
}
|
|
if strings.Contains(got, "FAIL m/x") {
|
|
t.Errorf("the dependency must not be reported as a package of its own:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestBuildFailureWithoutPackageEventIsStillReported(t *testing.T) {
|
|
events := `
|
|
{"Action":"build-output","ImportPath":"a [a.test]","Output":"a_test.go:7:2: undefined: nope\n"}
|
|
{"Action":"build-fail","ImportPath":"a [a.test]"}
|
|
`
|
|
got := feed(t, events)
|
|
for _, want := range []string{"FAIL a [build failed]", "==== build output of a ====", "undefined: nope"} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("output lacks %q:\n%s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCompilerPanicIsBuildOutputNotTestPanic(t *testing.T) {
|
|
events := `
|
|
{"Action":"build-output","ImportPath":"a [a.test]","Output":"# a [a.test]\npanic: internal compiler error\n\ngoroutine 1 [running]:\n"}
|
|
{"Action":"build-fail","ImportPath":"a [a.test]"}
|
|
{"Action":"start","Package":"a"}
|
|
{"Action":"output","Package":"a","Output":"FAIL\ta [build failed]\n"}
|
|
{"Action":"fail","Package":"a","Elapsed":0,"FailedBuild":"a [a.test]"}
|
|
`
|
|
got := feed(t, events)
|
|
if !strings.Contains(got, "==== build output of a ====\n # a [a.test]\n panic: internal compiler error") {
|
|
t.Errorf("compiler diagnostic missing from the build output block:\n%s", got)
|
|
}
|
|
if strings.Contains(got, "==== panic in") {
|
|
t.Errorf("compiler output must not be reported as a test panic:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestBuildVariantsOfOnePackageKeepSeparateOutput(t *testing.T) {
|
|
events := `
|
|
{"Action":"build-output","ImportPath":"a [a.test]","Output":"a.go:1:1: broken for a.test\n"}
|
|
{"Action":"build-output","ImportPath":"a [b.test]","Output":"a.go:1:1: broken for b.test\n"}
|
|
{"Action":"build-fail","ImportPath":"a [a.test]"}
|
|
{"Action":"build-fail","ImportPath":"a [b.test]"}
|
|
{"Action":"start","Package":"a"}
|
|
{"Action":"fail","Package":"a","Elapsed":0,"FailedBuild":"a [a.test]"}
|
|
{"Action":"start","Package":"b"}
|
|
{"Action":"fail","Package":"b","Elapsed":0,"FailedBuild":"a [b.test]"}
|
|
`
|
|
got := feed(t, events)
|
|
if strings.Count(got, "==== build output of a ====") != 2 {
|
|
t.Errorf("expected one output block per build variant:\n%s", got)
|
|
}
|
|
for _, want := range []string{"broken for a.test", "broken for b.test"} {
|
|
if strings.Count(got, want) != 1 {
|
|
t.Errorf("expected %q exactly once:\n%s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPassingPackageOutputIsNotPrinted(t *testing.T) {
|
|
events := `
|
|
{"Action":"output","Package":"a","Output":"level=info msg=\"noise between tests\"\n"}
|
|
{"Action":"pass","Package":"a","Elapsed":0.5}
|
|
`
|
|
got := feed(t, events)
|
|
if strings.Contains(got, "noise between tests") {
|
|
t.Errorf("package output of a passing package should stay quiet:\n%s", got)
|
|
}
|
|
}
|