cloner_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package main
  4. import (
  5. "reflect"
  6. "testing"
  7. "tailscale.com/cmd/cloner/clonerex"
  8. )
  9. func TestSliceContainer(t *testing.T) {
  10. num := 5
  11. examples := []struct {
  12. name string
  13. in *clonerex.SliceContainer
  14. }{
  15. {
  16. name: "nil",
  17. in: nil,
  18. },
  19. {
  20. name: "zero",
  21. in: &clonerex.SliceContainer{},
  22. },
  23. {
  24. name: "empty",
  25. in: &clonerex.SliceContainer{
  26. Slice: []*int{},
  27. },
  28. },
  29. {
  30. name: "nils",
  31. in: &clonerex.SliceContainer{
  32. Slice: []*int{nil, nil, nil, nil, nil},
  33. },
  34. },
  35. {
  36. name: "one",
  37. in: &clonerex.SliceContainer{
  38. Slice: []*int{&num},
  39. },
  40. },
  41. {
  42. name: "several",
  43. in: &clonerex.SliceContainer{
  44. Slice: []*int{&num, &num, &num, &num, &num},
  45. },
  46. },
  47. }
  48. for _, ex := range examples {
  49. t.Run(ex.name, func(t *testing.T) {
  50. out := ex.in.Clone()
  51. if !reflect.DeepEqual(ex.in, out) {
  52. t.Errorf("Clone() = %v, want %v", out, ex.in)
  53. }
  54. })
  55. }
  56. }
  57. func TestInterfaceContainer(t *testing.T) {
  58. examples := []struct {
  59. name string
  60. in *clonerex.InterfaceContainer
  61. }{
  62. {
  63. name: "nil",
  64. in: nil,
  65. },
  66. {
  67. name: "zero",
  68. in: &clonerex.InterfaceContainer{},
  69. },
  70. {
  71. name: "with_interface",
  72. in: &clonerex.InterfaceContainer{
  73. Interface: &clonerex.CloneableImpl{Value: 42},
  74. },
  75. },
  76. {
  77. name: "with_nil_interface",
  78. in: &clonerex.InterfaceContainer{
  79. Interface: nil,
  80. },
  81. },
  82. }
  83. for _, ex := range examples {
  84. t.Run(ex.name, func(t *testing.T) {
  85. out := ex.in.Clone()
  86. if !reflect.DeepEqual(ex.in, out) {
  87. t.Errorf("Clone() = %v, want %v", out, ex.in)
  88. }
  89. // Verify no aliasing: modifying the clone should not affect the original
  90. if ex.in != nil && ex.in.Interface != nil {
  91. if impl, ok := out.Interface.(*clonerex.CloneableImpl); ok {
  92. impl.Value = 999
  93. if origImpl, ok := ex.in.Interface.(*clonerex.CloneableImpl); ok {
  94. if origImpl.Value == 999 {
  95. t.Errorf("Clone() aliased memory with original")
  96. }
  97. }
  98. }
  99. }
  100. })
  101. }
  102. }
  103. func TestMapWithPointers(t *testing.T) {
  104. num1, num2 := 42, 100
  105. orig := &clonerex.MapWithPointers{
  106. Nested: map[string]*int{
  107. "foo": &num1,
  108. "bar": &num2,
  109. },
  110. WithCloneMethod: map[string]*clonerex.SliceContainer{
  111. "container1": {Slice: []*int{&num1, &num2}},
  112. "container2": {Slice: []*int{&num1}},
  113. },
  114. CloneInterface: map[string]clonerex.Cloneable{
  115. "impl1": &clonerex.CloneableImpl{Value: 123},
  116. "impl2": &clonerex.CloneableImpl{Value: 456},
  117. },
  118. }
  119. cloned := orig.Clone()
  120. if !reflect.DeepEqual(orig, cloned) {
  121. t.Errorf("Clone() = %v, want %v", cloned, orig)
  122. }
  123. // Mutate cloned.Nested pointer values
  124. *cloned.Nested["foo"] = 999
  125. if *orig.Nested["foo"] == 999 {
  126. t.Errorf("Clone() aliased memory in Nested: original was modified")
  127. }
  128. // Mutate cloned.WithCloneMethod slice values
  129. *cloned.WithCloneMethod["container1"].Slice[0] = 888
  130. if *orig.WithCloneMethod["container1"].Slice[0] == 888 {
  131. t.Errorf("Clone() aliased memory in WithCloneMethod: original was modified")
  132. }
  133. // Mutate cloned.CloneInterface values
  134. if impl, ok := cloned.CloneInterface["impl1"].(*clonerex.CloneableImpl); ok {
  135. impl.Value = 777
  136. if origImpl, ok := orig.CloneInterface["impl1"].(*clonerex.CloneableImpl); ok {
  137. if origImpl.Value == 777 {
  138. t.Errorf("Clone() aliased memory in CloneInterface: original was modified")
  139. }
  140. }
  141. }
  142. }
  143. func TestDeeplyNestedMap(t *testing.T) {
  144. num := 123
  145. orig := &clonerex.DeeplyNestedMap{
  146. ThreeLevels: map[string]map[string]map[string]int{
  147. "a": {
  148. "b": {"c": 1, "d": 2},
  149. "e": {"f": 3},
  150. },
  151. "g": {
  152. "h": {"i": 4},
  153. },
  154. },
  155. FourLevels: map[string]map[string]map[string]map[string]*clonerex.SliceContainer{
  156. "l1a": {
  157. "l2a": {
  158. "l3a": {
  159. "l4a": {Slice: []*int{&num}},
  160. "l4b": {Slice: []*int{&num, &num}},
  161. },
  162. },
  163. },
  164. },
  165. }
  166. cloned := orig.Clone()
  167. if !reflect.DeepEqual(orig, cloned) {
  168. t.Errorf("Clone() = %v, want %v", cloned, orig)
  169. }
  170. // Mutate the clone's ThreeLevels map
  171. cloned.ThreeLevels["a"]["b"]["c"] = 777
  172. if orig.ThreeLevels["a"]["b"]["c"] == 777 {
  173. t.Errorf("Clone() aliased memory in ThreeLevels: original was modified")
  174. }
  175. // Mutate the clone's FourLevels map at the deepest pointer level
  176. *cloned.FourLevels["l1a"]["l2a"]["l3a"]["l4a"].Slice[0] = 666
  177. if *orig.FourLevels["l1a"]["l2a"]["l3a"]["l4a"].Slice[0] == 666 {
  178. t.Errorf("Clone() aliased memory in FourLevels: original was modified")
  179. }
  180. // Add a new top-level key to the clone's FourLevels map
  181. newNum := 999
  182. cloned.FourLevels["l1b"] = map[string]map[string]map[string]*clonerex.SliceContainer{
  183. "l2b": {
  184. "l3b": {
  185. "l4c": {Slice: []*int{&newNum}},
  186. },
  187. },
  188. }
  189. if _, exists := orig.FourLevels["l1b"]; exists {
  190. t.Errorf("Clone() aliased FourLevels map: new top-level key appeared in original")
  191. }
  192. // Add a new nested key to the clone's FourLevels map
  193. cloned.FourLevels["l1a"]["l2a"]["l3a"]["l4c"] = &clonerex.SliceContainer{Slice: []*int{&newNum}}
  194. if _, exists := orig.FourLevels["l1a"]["l2a"]["l3a"]["l4c"]; exists {
  195. t.Errorf("Clone() aliased FourLevels map: new nested key appeared in original")
  196. }
  197. }