summaryrefslogtreecommitdiffhomepage
path: root/util/deephash/types_test.go
blob: 78b40d88e5094b50730cefa3d68e93e1a3779751 (plain)
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
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

package deephash

import (
	"io"
	"reflect"
	"testing"
	"time"
	"unsafe"

	"tailscale.com/tailcfg"
	"tailscale.com/types/structs"
)

func TestTypeIsMemHashable(t *testing.T) {
	tests := []struct {
		val  any
		want bool
	}{
		{true, true},
		{uint(1), true},
		{uint8(1), true},
		{uint16(1), true},
		{uint32(1), true},
		{uint64(1), true},
		{uintptr(1), true},
		{int(1), true},
		{int8(1), true},
		{int16(1), true},
		{int32(1), true},
		{int64(1), true},
		{float32(1), true},
		{float64(1), true},
		{complex64(1), true},
		{complex128(1), true},
		{[32]byte{}, true},
		{func() {}, false},
		{make(chan int), false},
		{struct{ io.Writer }{nil}, false},
		{unsafe.Pointer(nil), false},
		{new(int), false},
		{TwoInts{}, true},
		{[4]TwoInts{}, true},
		{IntThenByte{}, false},
		{[4]IntThenByte{}, false},
		{tailcfg.PortRange{}, true},
		{int16(0), true},
		{struct {
			_ int
			_ int
		}{}, true},
		{struct {
			_ int
			_ uint8
			_ int
		}{}, false}, // gap
		{struct {
			_ structs.Incomparable // if not last, zero-width
			x int
		}{}, true},
		{struct {
			x int
			_ structs.Incomparable // zero-width last: has space, can't memhash
		}{},
			false},
		{[0]chan bool{}, true},
		{struct{ f [0]func() }{}, true},
		{&selfHasherPointerRecv{}, false},
	}
	for _, tt := range tests {
		got := typeIsMemHashable(reflect.TypeOf(tt.val))
		if got != tt.want {
			t.Errorf("for type %T: got %v, want %v", tt.val, got, tt.want)
		}
	}
}

func TestTypeIsRecursive(t *testing.T) {
	type RecursiveStruct struct {
		_ *RecursiveStruct
	}
	type RecursiveChan chan *RecursiveChan

	tests := []struct {
		val  any
		want bool
	}{
		{val: 42, want: false},
		{val: "string", want: false},
		{val: 1 + 2i, want: false},
		{val: struct{}{}, want: false},
		{val: (*RecursiveStruct)(nil), want: true},
		{val: RecursiveStruct{}, want: true},
		{val: time.Unix(0, 0), want: false},
		{val: structs.Incomparable{}, want: false}, // ignore its [0]func()
		{val: tailcfg.NetPortRange{}, want: false}, // uses structs.Incomparable
		{val: (*tailcfg.Node)(nil), want: false},
		{val: map[string]bool{}, want: false},
		{val: func() {}, want: false},
		{val: make(chan int), want: false},
		{val: unsafe.Pointer(nil), want: false},
		{val: make(RecursiveChan), want: true},
		{val: make(chan int), want: false},
		{val: (*selfHasherPointerRecv)(nil), want: false},
	}
	for _, tt := range tests {
		got := typeIsRecursive(reflect.TypeOf(tt.val))
		if got != tt.want {
			t.Errorf("for type %T: got %v, want %v", tt.val, got, tt.want)
		}
	}
}