Merge branch 'tailscale:main' into main
commit
b76afe7c08
|
@ -33,6 +33,8 @@ type testAttempt struct {
|
||||||
outcome string // "pass", "fail", "skip"
|
outcome string // "pass", "fail", "skip"
|
||||||
logs bytes.Buffer
|
logs bytes.Buffer
|
||||||
isMarkedFlaky bool // set if the test is marked as flaky
|
isMarkedFlaky bool // set if the test is marked as flaky
|
||||||
|
|
||||||
|
pkgFinished bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type testName struct {
|
type testName struct {
|
||||||
|
@ -59,7 +61,12 @@ type goTestOutput struct {
|
||||||
|
|
||||||
var debug = os.Getenv("TS_TESTWRAPPER_DEBUG") != ""
|
var debug = os.Getenv("TS_TESTWRAPPER_DEBUG") != ""
|
||||||
|
|
||||||
func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []string) []*testAttempt {
|
// runTests runs the tests in pt and sends the results on ch. It sends a
|
||||||
|
// testAttempt for each test and a final testAttempt per pkg with pkgFinished
|
||||||
|
// set to true.
|
||||||
|
// It calls close(ch) when it's done.
|
||||||
|
func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []string, ch chan<- *testAttempt) {
|
||||||
|
defer close(ch)
|
||||||
args := []string{"test", "-json", pt.pattern}
|
args := []string{"test", "-json", pt.pattern}
|
||||||
args = append(args, otherArgs...)
|
args = append(args, otherArgs...)
|
||||||
if len(pt.tests) > 0 {
|
if len(pt.tests) > 0 {
|
||||||
|
@ -91,7 +98,6 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
|
||||||
|
|
||||||
jd := json.NewDecoder(r)
|
jd := json.NewDecoder(r)
|
||||||
resultMap := make(map[testName]*testAttempt)
|
resultMap := make(map[testName]*testAttempt)
|
||||||
var out []*testAttempt
|
|
||||||
for {
|
for {
|
||||||
var goOutput goTestOutput
|
var goOutput goTestOutput
|
||||||
if err := jd.Decode(&goOutput); err != nil {
|
if err := jd.Decode(&goOutput); err != nil {
|
||||||
|
@ -101,6 +107,16 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if goOutput.Test == "" {
|
if goOutput.Test == "" {
|
||||||
|
switch goOutput.Action {
|
||||||
|
case "fail", "pass", "skip":
|
||||||
|
ch <- &testAttempt{
|
||||||
|
name: testName{
|
||||||
|
pkg: goOutput.Package,
|
||||||
|
},
|
||||||
|
outcome: goOutput.Action,
|
||||||
|
pkgFinished: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := testName{
|
name := testName{
|
||||||
|
@ -123,7 +139,7 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
|
||||||
}
|
}
|
||||||
case "skip", "pass", "fail":
|
case "skip", "pass", "fail":
|
||||||
resultMap[name].outcome = goOutput.Action
|
resultMap[name].outcome = goOutput.Action
|
||||||
out = append(out, resultMap[name])
|
ch <- resultMap[name]
|
||||||
case "output":
|
case "output":
|
||||||
if strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage {
|
if strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage {
|
||||||
resultMap[name].isMarkedFlaky = true
|
resultMap[name].isMarkedFlaky = true
|
||||||
|
@ -133,7 +149,6 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<-done
|
<-done
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -174,58 +189,90 @@ func main() {
|
||||||
}
|
}
|
||||||
pattern, otherArgs := args[0], args[1:]
|
pattern, otherArgs := args[0], args[1:]
|
||||||
|
|
||||||
toRun := []*packageTests{ // packages still to test
|
type nextRun struct {
|
||||||
{pattern: pattern},
|
tests []*packageTests
|
||||||
|
attempt int
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgAttempts := make(map[string]int) // tracks how many times we've tried a package
|
toRun := []*nextRun{
|
||||||
|
{
|
||||||
|
tests: []*packageTests{{pattern: pattern}},
|
||||||
|
attempt: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
printPkgOutcome := func(pkg, outcome string, attempt int) {
|
||||||
|
if outcome == "skip" {
|
||||||
|
fmt.Printf("?\t%s [skipped/no tests] \n", pkg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if outcome == "pass" {
|
||||||
|
outcome = "ok"
|
||||||
|
}
|
||||||
|
if outcome == "fail" {
|
||||||
|
outcome = "FAIL"
|
||||||
|
}
|
||||||
|
if attempt > 1 {
|
||||||
|
fmt.Printf("%s\t%s [attempt=%d]\n", outcome, pkg, attempt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\t%s\n", outcome, pkg)
|
||||||
|
}
|
||||||
|
|
||||||
attempt := 0
|
|
||||||
for len(toRun) > 0 {
|
for len(toRun) > 0 {
|
||||||
attempt++
|
var thisRun *nextRun
|
||||||
var pt *packageTests
|
thisRun, toRun = toRun[0], toRun[1:]
|
||||||
pt, toRun = toRun[0], toRun[1:]
|
|
||||||
|
|
||||||
toRetry := make(map[string][]string) // pkg -> tests to retry
|
if thisRun.attempt >= maxAttempts {
|
||||||
|
fmt.Println("max attempts reached")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if thisRun.attempt > 1 {
|
||||||
|
fmt.Printf("\n\nAttempt #%d: Retrying flaky tests:\n\n", thisRun.attempt)
|
||||||
|
}
|
||||||
|
|
||||||
failed := false
|
failed := false
|
||||||
for _, tr := range runTests(ctx, attempt, pt, otherArgs) {
|
toRetry := make(map[string][]string) // pkg -> tests to retry
|
||||||
if *v || tr.outcome == "fail" {
|
for _, pt := range thisRun.tests {
|
||||||
io.Copy(os.Stderr, &tr.logs)
|
ch := make(chan *testAttempt)
|
||||||
}
|
go runTests(ctx, thisRun.attempt, pt, otherArgs, ch)
|
||||||
if tr.outcome != "fail" {
|
for tr := range ch {
|
||||||
continue
|
if tr.pkgFinished {
|
||||||
}
|
printPkgOutcome(tr.name.pkg, tr.outcome, thisRun.attempt)
|
||||||
if tr.isMarkedFlaky {
|
continue
|
||||||
toRetry[tr.name.pkg] = append(toRetry[tr.name.pkg], tr.name.name)
|
}
|
||||||
} else {
|
if *v || tr.outcome == "fail" {
|
||||||
failed = true
|
io.Copy(os.Stdout, &tr.logs)
|
||||||
|
}
|
||||||
|
if tr.outcome != "fail" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tr.isMarkedFlaky {
|
||||||
|
toRetry[tr.name.pkg] = append(toRetry[tr.name.pkg], tr.name.name)
|
||||||
|
} else {
|
||||||
|
failed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if failed {
|
if failed {
|
||||||
|
fmt.Println("\n\nNot retrying flaky tests because non-flaky tests failed.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if len(toRetry) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
pkgs := maps.Keys(toRetry)
|
pkgs := maps.Keys(toRetry)
|
||||||
sort.Strings(pkgs)
|
sort.Strings(pkgs)
|
||||||
|
nextRun := &nextRun{
|
||||||
|
attempt: thisRun.attempt + 1,
|
||||||
|
}
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
tests := toRetry[pkg]
|
tests := toRetry[pkg]
|
||||||
sort.Strings(tests)
|
sort.Strings(tests)
|
||||||
pkgAttempts[pkg]++
|
nextRun.tests = append(nextRun.tests, &packageTests{
|
||||||
if pkgAttempts[pkg] >= maxAttempts {
|
|
||||||
fmt.Println("Too many attempts for flaky tests:", pkg, tests)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Println("\nRetrying flaky tests:", pkg, tests)
|
|
||||||
toRun = append(toRun, &packageTests{
|
|
||||||
pattern: pkg,
|
pattern: pkg,
|
||||||
tests: tests,
|
tests: tests,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
toRun = append(toRun, nextRun)
|
||||||
}
|
}
|
||||||
for _, a := range pkgAttempts {
|
|
||||||
if a >= maxAttempts {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("PASS")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ func writePromExpVar(w io.Writer, prefix string, kv expvar.KeyValue) {
|
||||||
// IntMap uses expvar.Map on the inside, which presorts
|
// IntMap uses expvar.Map on the inside, which presorts
|
||||||
// keys. The output ordering is deterministic.
|
// keys. The output ordering is deterministic.
|
||||||
v.Do(func(kv expvar.KeyValue) {
|
v.Do(func(kv expvar.KeyValue) {
|
||||||
fmt.Fprintf(w, "%s{%s=%q} %v\n", name, v.Label, kv.Key, kv.Value)
|
fmt.Fprintf(w, "%s{%s=%q} %v\n", name, cmpx.Or(v.Label, "label"), kv.Key, kv.Value)
|
||||||
})
|
})
|
||||||
case *expvar.Map:
|
case *expvar.Map:
|
||||||
if label != "" && typ != "" {
|
if label != "" && typ != "" {
|
||||||
|
|
|
@ -165,6 +165,16 @@ func TestVarzHandler(t *testing.T) {
|
||||||
})(),
|
})(),
|
||||||
"control_save_config{reason=\"fun\"} 1\ncontrol_save_config{reason=\"new\"} 1\ncontrol_save_config{reason=\"updated\"} 1\n",
|
"control_save_config{reason=\"fun\"} 1\ncontrol_save_config{reason=\"new\"} 1\ncontrol_save_config{reason=\"updated\"} 1\n",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"metrics_label_map_unlabeled",
|
||||||
|
"foo",
|
||||||
|
(func() *metrics.LabelMap {
|
||||||
|
m := &metrics.LabelMap{Label: ""}
|
||||||
|
m.Add("a", 1)
|
||||||
|
return m
|
||||||
|
})(),
|
||||||
|
"foo{label=\"a\"} 1\n",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"expvar_label_map",
|
"expvar_label_map",
|
||||||
"counter_labelmap_keyname_m",
|
"counter_labelmap_keyname_m",
|
||||||
|
|
Loading…
Reference in New Issue