state: compare peer-visible Hostinfo through views

Field by field: no allocation, no reflection.

Updates #3531
This commit is contained in:
Kristoffer Dalby authored and Kristoffer Dalby committed 2026-10-09 11:44:37 +02:00
1 parent 9c764e5a99
commit 49b848a83d
3 files changed
+100 -24

No files matched your search

+52 -3
View File
@@ -3,6 +3,7 @@ package state
import (
"fmt"
"net/netip"
"reflect"
"strings"
"sync"
"sync/atomic"
@@ -860,12 +861,60 @@ func TestPeerHostinfoEqual(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
changed := base()
tt.mutate(changed)
require.Equal(t, tt.want, peerHostinfoEqual(base(), changed))
require.Equal(t, tt.want, peerHostinfoEqual(base().View(), changed.View()))
})
}
require.True(t, peerHostinfoEqual(nil, nil))
require.False(t, peerHostinfoEqual(nil, base()))
require.True(t, peerHostinfoEqual(tailcfg.HostinfoView{}, tailcfg.HostinfoView{}))
require.False(t, peerHostinfoEqual(tailcfg.HostinfoView{}, base().View()))
}
// TestPeerHostinfoEqualSeesEveryField fails when tailscale adds a field to
// [tailcfg.Service] or [tailcfg.Location] that servicesEqual or locationEqual
// does not compare.
func TestPeerHostinfoEqualSeesEveryField(t *testing.T) {
set := func(t *testing.T, f reflect.Value) {
t.Helper()
switch {
case f.Kind() == reflect.String:
f.SetString("x")
case f.CanInt():
f.SetInt(1)
case f.CanUint():
f.SetUint(1)
case f.CanFloat():
f.SetFloat(1)
default:
t.Fatalf("field kind %s not handled; compare it", f.Kind())
}
}
for _, field := range reflect.VisibleFields(reflect.TypeFor[tailcfg.Service]()) {
if !field.IsExported() {
continue
}
t.Run("Service."+field.Name, func(t *testing.T) {
var svc tailcfg.Service
set(t, reflect.ValueOf(&svc).Elem().FieldByIndex(field.Index))
a := &tailcfg.Hostinfo{Services: []tailcfg.Service{{}}}
b := &tailcfg.Hostinfo{Services: []tailcfg.Service{svc}}
assert.False(t, peerHostinfoEqual(a.View(), b.View()))
})
}
for _, field := range reflect.VisibleFields(reflect.TypeFor[tailcfg.Location]()) {
t.Run("Location."+field.Name, func(t *testing.T) {
var loc tailcfg.Location
set(t, reflect.ValueOf(&loc).Elem().FieldByIndex(field.Index))
a := &tailcfg.Hostinfo{Location: &tailcfg.Location{}}
b := &tailcfg.Hostinfo{Location: &loc}
assert.False(t, peerHostinfoEqual(a.View(), b.View()))
})
}
}
func TestNetInfoEqualIgnoringDERP(t *testing.T) {