types_test.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package deephash
  4. import (
  5. "io"
  6. "reflect"
  7. "testing"
  8. "time"
  9. "unsafe"
  10. "tailscale.com/tailcfg"
  11. "tailscale.com/types/structs"
  12. )
  13. func TestTypeIsMemHashable(t *testing.T) {
  14. tests := []struct {
  15. val any
  16. want bool
  17. }{
  18. {true, true},
  19. {uint(1), true},
  20. {uint8(1), true},
  21. {uint16(1), true},
  22. {uint32(1), true},
  23. {uint64(1), true},
  24. {uintptr(1), true},
  25. {int(1), true},
  26. {int8(1), true},
  27. {int16(1), true},
  28. {int32(1), true},
  29. {int64(1), true},
  30. {float32(1), true},
  31. {float64(1), true},
  32. {complex64(1), true},
  33. {complex128(1), true},
  34. {[32]byte{}, true},
  35. {func() {}, false},
  36. {make(chan int), false},
  37. {struct{ io.Writer }{nil}, false},
  38. {unsafe.Pointer(nil), false},
  39. {new(int), false},
  40. {TwoInts{}, true},
  41. {[4]TwoInts{}, true},
  42. {IntThenByte{}, false},
  43. {[4]IntThenByte{}, false},
  44. {tailcfg.PortRange{}, true},
  45. {int16(0), true},
  46. {struct {
  47. _ int
  48. _ int
  49. }{}, true},
  50. {struct {
  51. _ int
  52. _ uint8
  53. _ int
  54. }{}, false}, // gap
  55. {struct {
  56. _ structs.Incomparable // if not last, zero-width
  57. x int
  58. }{}, true},
  59. {struct {
  60. x int
  61. _ structs.Incomparable // zero-width last: has space, can't memhash
  62. }{},
  63. false},
  64. {[0]chan bool{}, true},
  65. {struct{ f [0]func() }{}, true},
  66. {&selfHasherPointerRecv{}, false},
  67. }
  68. for _, tt := range tests {
  69. got := typeIsMemHashable(reflect.TypeOf(tt.val))
  70. if got != tt.want {
  71. t.Errorf("for type %T: got %v, want %v", tt.val, got, tt.want)
  72. }
  73. }
  74. }
  75. func TestTypeIsRecursive(t *testing.T) {
  76. type RecursiveStruct struct {
  77. _ *RecursiveStruct
  78. }
  79. type RecursiveChan chan *RecursiveChan
  80. tests := []struct {
  81. val any
  82. want bool
  83. }{
  84. {val: 42, want: false},
  85. {val: "string", want: false},
  86. {val: 1 + 2i, want: false},
  87. {val: struct{}{}, want: false},
  88. {val: (*RecursiveStruct)(nil), want: true},
  89. {val: RecursiveStruct{}, want: true},
  90. {val: time.Unix(0, 0), want: false},
  91. {val: structs.Incomparable{}, want: false}, // ignore its [0]func()
  92. {val: tailcfg.NetPortRange{}, want: false}, // uses structs.Incomparable
  93. {val: (*tailcfg.Node)(nil), want: false},
  94. {val: map[string]bool{}, want: false},
  95. {val: func() {}, want: false},
  96. {val: make(chan int), want: false},
  97. {val: unsafe.Pointer(nil), want: false},
  98. {val: make(RecursiveChan), want: true},
  99. {val: make(chan int), want: false},
  100. {val: (*selfHasherPointerRecv)(nil), want: false},
  101. }
  102. for _, tt := range tests {
  103. got := typeIsRecursive(reflect.TypeOf(tt.val))
  104. if got != tt.want {
  105. t.Errorf("for type %T: got %v, want %v", tt.val, got, tt.want)
  106. }
  107. }
  108. }