1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package flowtrack
import (
"encoding/json"
"net/netip"
"testing"
"tailscale.com/tstest"
"tailscale.com/types/ipproto"
)
func TestCache(t *testing.T) {
c := &Cache[int]{MaxEntries: 2}
k1 := MakeTuple(0, netip.MustParseAddrPort("1.1.1.1:1"), netip.MustParseAddrPort("1.1.1.1:1"))
k2 := MakeTuple(0, netip.MustParseAddrPort("1.1.1.1:1"), netip.MustParseAddrPort("2.2.2.2:2"))
k3 := MakeTuple(0, netip.MustParseAddrPort("1.1.1.1:1"), netip.MustParseAddrPort("3.3.3.3:3"))
k4 := MakeTuple(0, netip.MustParseAddrPort("1.1.1.1:1"), netip.MustParseAddrPort("4.4.4.4:4"))
wantLen := func(want int) {
t.Helper()
if got := c.Len(); got != want {
t.Fatalf("Len = %d; want %d", got, want)
}
}
wantVal := func(key Tuple, want int) {
t.Helper()
got, ok := c.Get(key)
if !ok {
t.Fatalf("Get(%q) failed; want value %v", key, want)
}
if *got != want {
t.Fatalf("Get(%q) = %v; want %v", key, got, want)
}
}
wantMissing := func(key Tuple) {
t.Helper()
if got, ok := c.Get(key); ok {
t.Fatalf("Get(%q) = %v; want absent from cache", key, got)
}
}
wantLen(0)
c.RemoveOldest() // shouldn't panic
c.Remove(k4) // shouldn't panic
c.Add(k1, 1)
wantLen(1)
c.Add(k2, 2)
wantLen(2)
c.Add(k3, 3)
wantLen(2) // hit the max
wantMissing(k1)
c.Remove(k1)
wantLen(2) // no change; k1 should've been the deleted one per LRU
wantVal(k3, 3)
wantVal(k2, 2)
c.Remove(k2)
wantLen(1)
wantMissing(k2)
c.Add(k3, 30)
wantVal(k3, 30)
wantLen(1)
err := tstest.MinAllocsPerRun(t, 0, func() {
got, ok := c.Get(k3)
if !ok {
t.Fatal("missing k3")
}
if *got != 30 {
t.Fatalf("got = %d; want 30", got)
}
})
if err != nil {
t.Error(err)
}
}
func BenchmarkMapKeys(b *testing.B) {
b.Run("typed", func(b *testing.B) {
c := &Cache[struct{}]{MaxEntries: 1000}
var t Tuple
for proto := range 20 {
t = Tuple{proto: ipproto.Proto(proto), src: netip.MustParseAddr("1.1.1.1").As16(), srcPort: 1, dst: netip.MustParseAddr("1.1.1.1").As16(), dstPort: 1}
c.Add(t, struct{}{})
}
for i := 0; i < b.N; i++ {
_, ok := c.Get(t)
if !ok {
b.Fatal("missing key")
}
}
})
}
func TestStringJSON(t *testing.T) {
v := MakeTuple(123,
netip.MustParseAddrPort("1.2.3.4:5"),
netip.MustParseAddrPort("6.7.8.9:10"))
if got, want := v.String(), "(IPProto-123 1.2.3.4:5 => 6.7.8.9:10)"; got != want {
t.Errorf("String = %q; want %q", got, want)
}
got, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
const want = `{"proto":123,"src":"1.2.3.4:5","dst":"6.7.8.9:10"}`
if string(got) != want {
t.Errorf("Marshal = %q; want %q", got, want)
}
var back Tuple
if err := json.Unmarshal(got, &back); err != nil {
t.Fatal(err)
}
if back != v {
t.Errorf("back = %v; want %v", back, v)
}
}
|