hash_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package hash
  14. import (
  15. "fmt"
  16. "hash/adler32"
  17. "testing"
  18. "github.com/davecgh/go-spew/spew"
  19. )
  20. type A struct {
  21. x int
  22. y string
  23. }
  24. type B struct {
  25. x []int
  26. y map[string]bool
  27. }
  28. type C struct {
  29. x int
  30. y string
  31. }
  32. func (c C) String() string {
  33. return fmt.Sprintf("%d:%s", c.x, c.y)
  34. }
  35. func TestDeepHashObject(t *testing.T) {
  36. successCases := []func() interface{}{
  37. func() interface{} { return 8675309 },
  38. func() interface{} { return "Jenny, I got your number" },
  39. func() interface{} { return []string{"eight", "six", "seven"} },
  40. func() interface{} { return [...]int{5, 3, 0, 9} },
  41. func() interface{} { return map[int]string{8: "8", 6: "6", 7: "7"} },
  42. func() interface{} { return map[string]int{"5": 5, "3": 3, "0": 0, "9": 9} },
  43. func() interface{} { return A{867, "5309"} },
  44. func() interface{} { return &A{867, "5309"} },
  45. func() interface{} {
  46. return B{[]int{8, 6, 7}, map[string]bool{"5": true, "3": true, "0": true, "9": true}}
  47. },
  48. func() interface{} { return map[A]bool{A{8675309, "Jenny"}: true, A{9765683, "!Jenny"}: false} },
  49. func() interface{} { return map[C]bool{C{8675309, "Jenny"}: true, C{9765683, "!Jenny"}: false} },
  50. func() interface{} { return map[*A]bool{&A{8675309, "Jenny"}: true, &A{9765683, "!Jenny"}: false} },
  51. func() interface{} { return map[*C]bool{&C{8675309, "Jenny"}: true, &C{9765683, "!Jenny"}: false} },
  52. }
  53. for _, tc := range successCases {
  54. hasher1 := adler32.New()
  55. DeepHashObject(hasher1, tc())
  56. hash1 := hasher1.Sum32()
  57. DeepHashObject(hasher1, tc())
  58. hash2 := hasher1.Sum32()
  59. if hash1 != hash2 {
  60. t.Fatalf("hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash2)
  61. }
  62. for i := 0; i < 100; i++ {
  63. hasher2 := adler32.New()
  64. DeepHashObject(hasher1, tc())
  65. hash1a := hasher1.Sum32()
  66. DeepHashObject(hasher2, tc())
  67. hash2a := hasher2.Sum32()
  68. if hash1a != hash1 {
  69. t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash1a)
  70. }
  71. if hash2a != hash2 {
  72. t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash2, hash2a)
  73. }
  74. if hash1a != hash2a {
  75. t.Errorf("hash of the same object produced (%q) different results: %d vs %d", toString(tc()), hash1a, hash2a)
  76. }
  77. }
  78. }
  79. }
  80. func toString(obj interface{}) string {
  81. return spew.Sprintf("%#v", obj)
  82. }
  83. type wheel struct {
  84. radius uint32
  85. }
  86. type unicycle struct {
  87. primaryWheel *wheel
  88. licencePlateID string
  89. tags map[string]string
  90. }
  91. func TestDeepObjectPointer(t *testing.T) {
  92. // Arrange
  93. wheel1 := wheel{radius: 17}
  94. wheel2 := wheel{radius: 22}
  95. wheel3 := wheel{radius: 17}
  96. myUni1 := unicycle{licencePlateID: "blah", primaryWheel: &wheel1, tags: map[string]string{"color": "blue", "name": "john"}}
  97. myUni2 := unicycle{licencePlateID: "blah", primaryWheel: &wheel2, tags: map[string]string{"color": "blue", "name": "john"}}
  98. myUni3 := unicycle{licencePlateID: "blah", primaryWheel: &wheel3, tags: map[string]string{"color": "blue", "name": "john"}}
  99. // Run it more than once to verify determinism of hasher.
  100. for i := 0; i < 100; i++ {
  101. hasher1 := adler32.New()
  102. hasher2 := adler32.New()
  103. hasher3 := adler32.New()
  104. // Act
  105. DeepHashObject(hasher1, myUni1)
  106. hash1 := hasher1.Sum32()
  107. DeepHashObject(hasher1, myUni1)
  108. hash1a := hasher1.Sum32()
  109. DeepHashObject(hasher2, myUni2)
  110. hash2 := hasher2.Sum32()
  111. DeepHashObject(hasher3, myUni3)
  112. hash3 := hasher3.Sum32()
  113. // Assert
  114. if hash1 != hash1a {
  115. t.Errorf("repeated hash of the same object produced different results: %d vs %d", hash1, hash1a)
  116. }
  117. if hash1 == hash2 {
  118. t.Errorf("hash1 (%d) and hash2(%d) must be different because they have different values for wheel size", hash1, hash2)
  119. }
  120. if hash1 != hash3 {
  121. t.Errorf("hash1 (%d) and hash3(%d) must be the same because although they point to different objects, they have the same values for wheel size", hash1, hash3)
  122. }
  123. }
  124. }