tailscale/portlist/portlist_test.go
Brad Fitzpatrick 036f70b7b4 portlist: refactor, introduce OS-specific types
Add an osImpl interface that can be stateful and thus more efficient
between calls. It will later be implemented by all OSes but for now
this change only adds a Linux implementation.

Remove Port.inode. It was only used by Linux and moves into its osImpl.

Don't reopen /proc/net/* files on each run. Turns out you can just
keep then open and seek to the beginning and reread and the contents
are fresh.

    name                    old time/op    new time/op    delta
    GetListIncremental-8    7.29ms ± 2%    6.53ms ± 1%  -10.50%  (p=0.000 n=9+9)

    name                   old alloc/op   new alloc/op   delta
    GetListIncremental-8    1.30kB ±13%    0.70kB ± 5%  -46.38%  (p=0.000 n=9+10)

    name                  old allocs/op  new allocs/op  delta
    GetListIncremental-8      33.2 ±11%      18.0 ± 0%  -45.82%  (p=0.000 n=9+10)

Updates #5958

Change-Id: I4be83463cbd23c2e2fa5d0bdf38560004f53401b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-10-23 20:29:23 -07:00

156 lines
3.2 KiB
Go

// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package portlist
import (
"net"
"testing"
"tailscale.com/tstest"
)
func TestGetList(t *testing.T) {
tstest.ResourceCheck(t)
var p Poller
pl, err := p.getList()
if err != nil {
t.Fatal(err)
}
for i, p := range pl {
t.Logf("[%d] %+v", i, p)
}
t.Logf("As String: %v", pl.String())
}
func TestIgnoreLocallyBoundPorts(t *testing.T) {
tstest.ResourceCheck(t)
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Skipf("failed to bind: %v", err)
}
defer ln.Close()
ta := ln.Addr().(*net.TCPAddr)
port := ta.Port
var p Poller
pl, err := p.getList()
if err != nil {
t.Fatal(err)
}
for _, p := range pl {
if p.Proto == "tcp" && int(p.Port) == port {
t.Fatal("didn't expect to find test's localhost ephemeral port")
}
}
}
func TestEqualLessThan(t *testing.T) {
tests := []struct {
name string
a, b Port
want bool
}{
{
"Port a < b",
Port{Proto: "tcp", Port: 100, Process: "proc1"},
Port{Proto: "tcp", Port: 101, Process: "proc1"},
true,
},
{
"Port a > b",
Port{Proto: "tcp", Port: 101, Process: "proc1"},
Port{Proto: "tcp", Port: 100, Process: "proc1"},
false,
},
{
"Proto a < b",
Port{Proto: "tcp", Port: 100, Process: "proc1"},
Port{Proto: "udp", Port: 100, Process: "proc1"},
true,
},
{
"Proto a < b",
Port{Proto: "udp", Port: 100, Process: "proc1"},
Port{Proto: "tcp", Port: 100, Process: "proc1"},
false,
},
{
"Process a < b",
Port{Proto: "tcp", Port: 100, Process: "proc1"},
Port{Proto: "tcp", Port: 100, Process: "proc2"},
true,
},
{
"Process a > b",
Port{Proto: "tcp", Port: 100, Process: "proc2"},
Port{Proto: "tcp", Port: 100, Process: "proc1"},
false,
},
{
"Port evaluated first",
Port{Proto: "udp", Port: 100, Process: "proc2"},
Port{Proto: "tcp", Port: 101, Process: "proc1"},
true,
},
{
"Proto evaluated second",
Port{Proto: "tcp", Port: 100, Process: "proc2"},
Port{Proto: "udp", Port: 100, Process: "proc1"},
true,
},
{
"Process evaluated fourth",
Port{Proto: "tcp", Port: 100, Process: "proc1"},
Port{Proto: "tcp", Port: 100, Process: "proc2"},
true,
},
{
"equal",
Port{Proto: "tcp", Port: 100, Process: "proc1"},
Port{Proto: "tcp", Port: 100, Process: "proc1"},
false,
},
}
for _, tt := range tests {
got := tt.a.lessThan(&tt.b)
if got != tt.want {
t.Errorf("%s: Equal = %v; want %v", tt.name, got, tt.want)
}
lessBack := tt.b.lessThan(&tt.a)
if got && lessBack {
t.Errorf("%s: both a and b report being less than each other", tt.name)
}
wantEqual := !got && !lessBack
gotEqual := tt.a.equal(&tt.b)
if gotEqual != wantEqual {
t.Errorf("%s: equal = %v; want %v", tt.name, gotEqual, wantEqual)
}
}
}
func BenchmarkGetList(b *testing.B) {
benchmarkGetList(b, false)
}
func BenchmarkGetListIncremental(b *testing.B) {
benchmarkGetList(b, true)
}
func benchmarkGetList(b *testing.B, incremental bool) {
b.ReportAllocs()
var p Poller
for i := 0; i < b.N; i++ {
pl, err := p.getList()
if err != nil {
b.Fatal(err)
}
if incremental {
p.prev = pl
}
}
}