tailscale/wgengine/watchdog_test.go
Brad Fitzpatrick 5ef3713c9f cmd/vet: add subtestnames analyzer; fix all existing violations
Add a new vet analyzer that checks t.Run subtest names don't contain
characters requiring quoting when re-running via "go test -run". This
enforces the style guide rule: don't use spaces or punctuation in
subtest names.

The analyzer flags:
- Direct t.Run calls with string literal names containing spaces,
  regex metacharacters, quotes, or other problematic characters
- Table-driven t.Run(tt.name, ...) calls where tt ranges over a
  slice/map literal with bad name field values

Also fix all 978 existing violations across 81 test files, replacing
spaces with hyphens and shortening long sentence-like names to concise
hyphenated forms.

Updates #19242

Change-Id: Ib0ad96a111bd8e764582d1d4902fe2599454ab65
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-04-05 15:52:51 -07:00

142 lines
3.1 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !js
package wgengine
import (
"runtime"
"sync"
"testing"
"time"
"tailscale.com/health"
"tailscale.com/util/eventbus/eventbustest"
"tailscale.com/util/usermetric"
)
func TestWatchdog(t *testing.T) {
t.Parallel()
var maxWaitMultiple time.Duration = 1
if runtime.GOOS == "darwin" {
// Work around slow close syscalls on Big Sur with content filter Network Extensions installed.
// See https://github.com/tailscale/tailscale/issues/1598.
maxWaitMultiple = 15
}
t.Run("default-watchdog-does-not-fire", func(t *testing.T) {
t.Parallel()
bus := eventbustest.NewBus(t)
ht := health.NewTracker(bus)
reg := new(usermetric.Registry)
e, err := NewFakeUserspaceEngine(t.Logf, 0, ht, reg, bus)
if err != nil {
t.Fatal(err)
}
e = NewWatchdog(e)
e.(*watchdogEngine).maxWait = maxWaitMultiple * 150 * time.Millisecond
e.(*watchdogEngine).logf = t.Logf
e.(*watchdogEngine).fatalf = t.Fatalf
e.RequestStatus()
e.RequestStatus()
e.RequestStatus()
e.Close()
})
}
func TestWatchdogMetrics(t *testing.T) {
tests := []struct {
name string
events []watchdogEvent
wantCounts map[watchdogEvent]int64
}{
{
name: "single-event-types",
events: []watchdogEvent{RequestStatus, PeerForIPEvent, Ping},
wantCounts: map[watchdogEvent]int64{
RequestStatus: 1,
PeerForIPEvent: 1,
Ping: 1,
},
},
{
name: "repeated-events",
events: []watchdogEvent{RequestStatus, RequestStatus, Ping, RequestStatus},
wantCounts: map[watchdogEvent]int64{
RequestStatus: 3,
Ping: 1,
},
},
}
// For swallowing fatalf calls and stack traces
logf := func(format string, args ...any) {}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
clearMetrics(t)
bus := eventbustest.NewBus(t)
ht := health.NewTracker(bus)
reg := new(usermetric.Registry)
e, err := NewFakeUserspaceEngine(logf, 0, ht, reg, bus)
if err != nil {
t.Fatal(err)
}
e = NewWatchdog(e)
w := e.(*watchdogEngine)
w.maxWait = 1 * time.Microsecond
w.logf = logf
w.fatalf = logf
var wg sync.WaitGroup
wg.Add(len(tt.events))
for _, ev := range tt.events {
blocked := make(chan struct{})
w.watchdog(ev, func() {
defer wg.Done()
<-blocked
})
close(blocked)
}
wg.Wait()
// Check individual event counts
for ev, want := range tt.wantCounts {
m, ok := watchdogMetrics[ev]
if !ok {
t.Fatalf("no metric found for event %q", ev)
}
got := m.Value()
if got != want {
t.Errorf("got %d metric events for %q, want %d", got, ev, want)
}
}
// Check total count for Any
m, ok := watchdogMetrics[Any]
if !ok {
t.Fatalf("no Any metric found")
}
got := m.Value()
if got != int64(len(tt.events)) {
t.Errorf("got %d metric events for Any, want %d", got, len(tt.events))
}
})
}
}
func clearMetrics(t *testing.T) {
t.Helper()
if watchdogMetrics == nil {
return
}
for _, m := range watchdogMetrics {
m.Set(0)
}
}